use of com.github.ambry.account.Container in project ambry by linkedin.
the class MySqlAccountServiceIntegrationTest method testAccountRefresh.
/**
* Producer-consumer test for multiple account services.
*/
@Test
public void testAccountRefresh() throws Exception {
MySqlAccountService producerAccountService = mySqlAccountService;
MySqlAccountStore producerAccountStore = mySqlAccountStore;
// Create second account service with scheduled polling disabled
mySqlConfigProps.setProperty(UPDATER_POLLING_INTERVAL_SECONDS, "0");
accountServiceConfig = new MySqlAccountServiceConfig(new VerifiableProperties(mySqlConfigProps));
MySqlAccountStore consumerAccountStore = spy(new MySqlAccountStoreFactory(new VerifiableProperties(mySqlConfigProps), new MetricRegistry()).getMySqlAccountStore());
MySqlAccountStoreFactory mockMySqlAccountStoreFactory = mock(MySqlAccountStoreFactory.class);
when(mockMySqlAccountStoreFactory.getMySqlAccountStore()).thenReturn(consumerAccountStore);
AccountServiceMetrics accountServiceMetrics = new AccountServiceMetrics(new MetricRegistry());
MySqlAccountService consumerAccountService = new MySqlAccountService(accountServiceMetrics, accountServiceConfig, mockMySqlAccountStoreFactory, mockNotifier);
// Add account with 3 containers
short accountId = 101;
String accountName = "a1";
// Number of calls expected in producer account store
List<Container> containers = new ArrayList<>();
containers.add(new ContainerBuilder((short) 1, "c1", ContainerStatus.ACTIVE, DESCRIPTION, accountId).build());
containers.add(new ContainerBuilder((short) 2, "c2", ContainerStatus.ACTIVE, DESCRIPTION, accountId).build());
containers.add(new ContainerBuilder((short) 3, "c3", ContainerStatus.ACTIVE, DESCRIPTION, accountId).build());
Account a1 = new AccountBuilder(accountId, accountName, Account.AccountStatus.ACTIVE).containers(containers).build();
producerAccountService.updateAccounts(Collections.singletonList(a1));
Account finalA = a1;
verify(producerAccountStore).updateAccounts(argThat(accountInfos -> {
AccountUpdateInfo accountInfo = accountInfos.get(0);
return accountInfo.getAccount().equals(finalA) && accountInfo.getAddedContainers().equals(containers) && accountInfo.isAdded() && !accountInfo.isUpdated() && accountInfo.getUpdatedContainers().isEmpty();
}));
// Note: because consumer is notified of changes, its cache should already be in sync with DB
long lmt = consumerAccountService.accountInfoMapRef.get().getLastModifiedTime();
assertEquals("Account mismatch", a1, consumerAccountService.getAccountByName(accountName));
// Update account only
String newAccountName = "a1-updated";
a1 = new AccountBuilder(a1).name(newAccountName).build();
producerAccountService.updateAccounts(Collections.singletonList(a1));
Account finalA1 = a1;
verify(producerAccountStore).updateAccounts(argThat(accountInfos -> {
AccountUpdateInfo accountInfo = accountInfos.get(0);
return accountInfo.getAccount().equals(finalA1) && accountInfo.getAddedContainers().isEmpty() && !accountInfo.isAdded() && accountInfo.isUpdated() && accountInfo.getUpdatedContainers().isEmpty();
}));
verify(consumerAccountStore).getNewAccounts(eq(lmt));
verify(consumerAccountStore).getNewContainers(eq(lmt));
assertEquals("Account mismatch", a1, consumerAccountService.getAccountByName(newAccountName));
assertNull("Expected no account with old name", consumerAccountService.getAccountByName(accountName));
accountName = newAccountName;
// Update container only
Container c1Mod = new ContainerBuilder(containers.get(0)).setStatus(ContainerStatus.DELETE_IN_PROGRESS).build();
containers.set(0, c1Mod);
a1 = new AccountBuilder(a1).containers(containers).build();
Collection<Container> result = producerAccountService.updateContainers(accountName, Collections.singletonList(c1Mod));
// Account should not have been touched
verify(producerAccountStore).updateAccounts(argThat(accountsInfo -> accountsInfo.get(0).getUpdatedContainers().size() == 1));
assertEquals("Expected one result", 1, result.size());
assertEquals("Container mismatch", c1Mod, result.iterator().next());
verify(consumerAccountStore).getNewAccounts(eq(lmt));
verify(consumerAccountStore).getNewContainers(eq(lmt));
assertEquals("Container mismatch", c1Mod, consumerAccountService.getContainerByName(accountName, "c1"));
assertEquals("Account mismatch", a1, consumerAccountService.getAccountByName(accountName));
lmt = consumerAccountService.accountInfoMapRef.get().getLastModifiedTime();
// Add container only
Container cNew = makeNewContainer("c4", accountId, ContainerStatus.ACTIVE);
result = producerAccountService.updateContainers(accountName, Collections.singletonList(cNew));
verify(producerAccountStore).updateAccounts(argThat(accountsInfo -> accountsInfo.get(0).getAddedContainers().size() == 1));
assertEquals("Expected one result", 1, result.size());
cNew = result.iterator().next();
containers.add(cNew);
a1 = new AccountBuilder(a1).containers(containers).build();
verify(consumerAccountStore).getNewAccounts(eq(lmt));
verify(consumerAccountStore).getNewContainers(eq(lmt));
assertEquals("Container mismatch", cNew, consumerAccountService.getContainerByName(accountName, "c4"));
assertEquals("Account mismatch", a1, consumerAccountService.getAccountByName(accountName));
// For this section, consumer must get out of date so need to unsubscribe its notifier
mockNotifier.unsubscribeAll();
// TODO:
// Add container in AS1, call AS2.getContainer() to force fetch
// Add C1 in AS1, add C1 in AS2 (should succeed and return existing id)
Container cNewProd = makeNewContainer("c5", accountId, ContainerStatus.ACTIVE);
result = producerAccountService.updateContainers(accountName, Collections.singletonList(cNewProd));
short newId = result.iterator().next().getId();
// Add the same container to second AS with stale cache
// Expect it to fail first time (conflict), refresh cache and succeed on retry
result = consumerAccountService.updateContainers(accountName, Collections.singletonList(cNewProd));
assertEquals(newId, result.iterator().next().getId());
assertEquals(1, accountServiceMetrics.updateAccountErrorCount.getCount());
assertEquals(1, accountServiceMetrics.conflictRetryCount.getCount());
// Add C1 in AS1, add C2 in AS2
cNewProd = makeNewContainer("c6", accountId, ContainerStatus.ACTIVE);
result = producerAccountService.updateContainers(accountName, Collections.singletonList(cNewProd));
newId = result.iterator().next().getId();
Container cNewCons = makeNewContainer("c7", accountId, ContainerStatus.ACTIVE);
result = consumerAccountService.updateContainers(accountName, Collections.singletonList(cNewCons));
assertNotSame(newId, result.iterator().next().getId());
assertEquals(2, accountServiceMetrics.updateAccountErrorCount.getCount());
assertEquals(2, accountServiceMetrics.conflictRetryCount.getCount());
// Check gauge values
assertTrue("Sync time not updated", accountServiceMetrics.timeInSecondsSinceLastSyncGauge.getValue() < 10);
assertEquals("Unexpected container count", 7, accountServiceMetrics.containerCountGauge.getValue().intValue());
}
use of com.github.ambry.account.Container in project ambry by linkedin.
the class MySqlAccountServiceIntegrationTest method testUpdateAccounts.
/**
* Tests creating and updating accounts through {@link MySqlAccountService}:
* 1. add a new {@link Account};
* 2. update existing {@link Account} by adding new {@link Container} to an existing {@link Account};
*/
@Test
public void testUpdateAccounts() throws Exception {
Container testContainer = new ContainerBuilder((short) 1, "testContainer", Container.ContainerStatus.ACTIVE, "testContainer", (short) 1).build();
Account testAccount = new AccountBuilder((short) 1, "testAccount", Account.AccountStatus.ACTIVE).containers(Collections.singleton(testContainer)).build();
// 1. Addition of new account. Verify account is added to cache.
mySqlAccountService.updateAccounts(Collections.singletonList(testAccount));
List<Account> accounts = new ArrayList<>(mySqlAccountService.getAllAccounts());
assertEquals("Mismatch in number of accounts", 1, accounts.size());
assertEquals("Mismatch in account retrieved by ID", testAccount, mySqlAccountService.getAccountById(testAccount.getId()));
assertEquals("Mismatch in account retrieved by name", testAccount, mySqlAccountService.getAccountByName(testAccount.getName()));
// 2. Update existing account by adding new container. Verify account is updated in cache.
Container testContainer2 = new ContainerBuilder((short) 2, "testContainer2", Container.ContainerStatus.ACTIVE, "testContainer2", (short) 1).build();
testAccount = new AccountBuilder(testAccount).addOrUpdateContainer(testContainer2).build();
mySqlAccountService.updateAccounts(Collections.singletonList(testAccount));
assertEquals("Mismatch in account retrieved by ID", testAccount, mySqlAccountService.getAccountById(testAccount.getId()));
// 3. Update existing container. Verify container is updated in cache.
testContainer = new ContainerBuilder(testContainer).setMediaScanDisabled(true).setCacheable(true).build();
testAccount = new AccountBuilder(testAccount).addOrUpdateContainer(testContainer).build();
mySqlAccountService.updateAccounts(Collections.singletonList(testAccount));
assertEquals("Mismatch in account retrieved by ID", testAccount, mySqlAccountService.getAccountById(testAccount.getId()));
}
use of com.github.ambry.account.Container in project ambry by linkedin.
the class MySqlAccountServiceIntegrationTest method testLRUCacheForNotFoundContainers.
/**
* Test LRU cache for containers not found recently.
*/
@Test
public void testLRUCacheForNotFoundContainers() throws Exception {
// Add new account on account service
short accountId = 101;
String accountName = "a1";
Container container = new ContainerBuilder((short) 1, "c1", ContainerStatus.ACTIVE, DESCRIPTION, accountId).build();
Account account = new AccountBuilder(accountId, accountName, Account.AccountStatus.ACTIVE).containers(Collections.singletonList(container)).build();
mySqlAccountService.updateAccounts(Collections.singletonList(account));
// Look up container "c2" in account service
assertNull("Container must not be present in account service", mySqlAccountService.getContainerByName(accountName, "c2"));
// verify call to query container from mysql db
verify(mySqlAccountStore).getContainerByName(eq((int) accountId), eq("c2"));
// verify container name "a1:c2" is added to LRU cache
assertTrue("container a1:c2 must be present in LRU cache", mySqlAccountService.getRecentNotFoundContainersCache().contains("a1" + MySqlAccountService.SEPARATOR + "c2"));
// Look up container "c2" again in account service
assertNull("Container must not be present in account service", mySqlAccountService.getContainerByName(accountName, "c2"));
// verify mysql is not queried this time
verify(mySqlAccountStore, times(1)).getContainerByName(anyInt(), anyString());
// Add container "c2" to account service
mySqlAccountService.updateContainers(accountName, Collections.singletonList(new ContainerBuilder((short) -1, "c2", ContainerStatus.ACTIVE, DESCRIPTION, accountId).build()));
// verify container "c2" is removed from not-found LRU cache
assertFalse("Added container a1:c2 must no longer be present in LRU cache", mySqlAccountService.getRecentNotFoundContainersCache().contains("a1" + MySqlAccountService.SEPARATOR + "c2"));
}
use of com.github.ambry.account.Container in project ambry by linkedin.
the class MySqlAccountServiceIntegrationTest method makeTestAccountWithContainer.
private Account makeTestAccountWithContainer() {
Container testContainer = new ContainerBuilder((short) 1, "testContainer", Container.ContainerStatus.ACTIVE, "testContainer", (short) 1).build();
Account testAccount = new AccountBuilder((short) 1, "testAccount", Account.AccountStatus.ACTIVE).containers(Collections.singleton(testContainer)).build();
return testAccount;
}
use of com.github.ambry.account.Container 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());
}
}
Aggregations