Search in sources :

Example 61 with Container

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());
}
Also used : AccountUpdateInfo(com.github.ambry.account.AccountUtils.AccountUpdateInfo) Arrays(java.util.Arrays) Connection(java.sql.Connection) MySqlAccountStoreFactory(com.github.ambry.account.mysql.MySqlAccountStoreFactory) ArrayList(java.util.ArrayList) SQLException(java.sql.SQLException) JSONObject(org.json.JSONObject) TestUtils(com.github.ambry.utils.TestUtils) SystemTime(com.github.ambry.utils.SystemTime) AccountTestUtils(com.github.ambry.utils.AccountTestUtils) Path(java.nio.file.Path) Container(com.github.ambry.account.Container) MetricRegistry(com.codahale.metrics.MetricRegistry) Properties(java.util.Properties) MySqlAccountStore(com.github.ambry.account.mysql.MySqlAccountStore) VerifiableProperties(com.github.ambry.config.VerifiableProperties) Collection(java.util.Collection) Utils(com.github.ambry.utils.Utils) IOException(java.io.IOException) Test(org.junit.Test) MySqlAccountServiceConfig(com.github.ambry.config.MySqlAccountServiceConfig) MySqlDataAccessor(com.github.ambry.mysql.MySqlDataAccessor) MySqlUtils(com.github.ambry.mysql.MySqlUtils) Mockito(org.mockito.Mockito) List(java.util.List) Paths(java.nio.file.Paths) AccountDao(com.github.ambry.account.mysql.AccountDao) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) MySqlMetrics(com.github.ambry.mysql.MySqlMetrics) Statement(java.sql.Statement) Assert(org.junit.Assert) Collections(java.util.Collections) JSONArray(org.json.JSONArray) MySqlAccountServiceConfig(com.github.ambry.config.MySqlAccountServiceConfig) VerifiableProperties(com.github.ambry.config.VerifiableProperties) MetricRegistry(com.codahale.metrics.MetricRegistry) ArrayList(java.util.ArrayList) MySqlAccountStoreFactory(com.github.ambry.account.mysql.MySqlAccountStoreFactory) Container(com.github.ambry.account.Container) AccountUpdateInfo(com.github.ambry.account.AccountUtils.AccountUpdateInfo) MySqlAccountStore(com.github.ambry.account.mysql.MySqlAccountStore) Test(org.junit.Test)

Example 62 with Container

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()));
}
Also used : Container(com.github.ambry.account.Container) ArrayList(java.util.ArrayList) Test(org.junit.Test)

Example 63 with Container

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"));
}
Also used : Container(com.github.ambry.account.Container) Test(org.junit.Test)

Example 64 with Container

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;
}
Also used : Container(com.github.ambry.account.Container)

Example 65 with Container

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());
    }
}
Also used : Container(com.github.ambry.account.Container) ZNRecord(org.apache.helix.zookeeper.datamodel.ZNRecord) Test(org.junit.Test)

Aggregations

Container (com.github.ambry.account.Container)119 Account (com.github.ambry.account.Account)88 Test (org.junit.Test)61 ArrayList (java.util.ArrayList)30 RestServiceException (com.github.ambry.rest.RestServiceException)20 ContainerBuilder (com.github.ambry.account.ContainerBuilder)17 JSONObject (org.json.JSONObject)17 VerifiableProperties (com.github.ambry.config.VerifiableProperties)16 HashSet (java.util.HashSet)15 HashMap (java.util.HashMap)14 Properties (java.util.Properties)14 AccountBuilder (com.github.ambry.account.AccountBuilder)13 RestRequest (com.github.ambry.rest.RestRequest)13 ByteBuffer (java.nio.ByteBuffer)13 Map (java.util.Map)13 MetricRegistry (com.codahale.metrics.MetricRegistry)12 TestUtils (com.github.ambry.utils.TestUtils)12 Collections (java.util.Collections)12 List (java.util.List)12 Assert (org.junit.Assert)12