blob: 6051691a19a0dc802ad4e79036a32c888057e7d8 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.distributed;
import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.catchThrowable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.apache.geode.distributed.internal.DistributionManager;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.test.dunit.AsyncInvocation;
import org.apache.geode.test.dunit.SerializableRunnableIF;
import org.apache.geode.test.dunit.rules.ClusterStartupRule;
import org.apache.geode.test.dunit.rules.MemberVM;
public class GrantorFailoverDUnitTest {
private final List<MemberVM> locators = new ArrayList<>();
@Rule
public ClusterStartupRule clusterStartupRule = new ClusterStartupRule();
public static final String SERVICE_NAME = "serviceName";
@Before
public void before() {
locators.add(clusterStartupRule.startLocatorVM(0));
locators.add(clusterStartupRule.startLocatorVM(1, locators.get(0).getPort()));
locators.add(clusterStartupRule.startLocatorVM(2, locators.get(0).getPort()));
for (MemberVM locator : locators) {
locator.invoke((SerializableRunnableIF) () -> DistributedLockService.create(SERVICE_NAME,
ClusterStartupRule.getCache().getDistributedSystem()));
}
}
@After
public void cleanup() {
for (MemberVM locator : locators) {
locator.invoke(() -> DistributedLockService.destroy(SERVICE_NAME));
}
}
@Test
public void cannotUnlockALockLockedByAnotherVm() {
final String lock0 = "lock 0";
final String lock1 = "lock 1";
final AtomicBoolean lock0Status = new AtomicBoolean(false);
final AtomicBoolean lock1Status = new AtomicBoolean(false);
lock0Status.set(locators.get(0)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock0, 20, -1)));
lock1Status.set(locators.get(1)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock1, 20, -1)));
assertThat(lock0Status.get()).isTrue();
assertThat(lock1Status.get()).isTrue();
lock1Status.set(locators.get(0)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock1, 20, -1)));
lock0Status.set(locators.get(1)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock0, 20, -1)));
assertThat(lock0Status.get()).isFalse();
assertThat(lock1Status.get()).isFalse();
assertThatThrownBy(() -> locators.get(0)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock1)))
.hasCauseInstanceOf(LockNotHeldException.class);
assertThatThrownBy(() -> locators.get(1)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock0)))
.hasCauseInstanceOf(LockNotHeldException.class);
assertThat(catchThrowable(() -> locators.get(0)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock0))))
.isNull();
assertThat(catchThrowable(() -> locators.get(1)
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock1))))
.isNull();
}
@Test
public void lockRecoveryAfterGrantorDies() throws Exception {
final String lock1 = "lock 1";
final String lock2 = "lock 2";
final String lock3 = "lock 3";
locators.get(0).invoke(GrantorFailoverDUnitTest::assertIsElderAndGetId);
// Grantor but not the elder
final MemberVM grantorVM = locators.get(1);
final MemberVM survivor1 = locators.get(0);
final MemberVM survivor2 = locators.get(2);
grantorVM.invoke(() -> {
DistributedLockService.becomeLockGrantor(SERVICE_NAME);
await().untilAsserted(
() -> assertThat(DistributedLockService.isLockGrantor(SERVICE_NAME)).isTrue());
});
assertThat(survivor1
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock1, 20_000, -1)))
.isTrue();
assertThat(survivor2
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock2, 20_000, -1)))
.isTrue();
clusterStartupRule.crashVM(1);
locators.remove(grantorVM);
// can't get the locks again
assertThat(survivor2
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock1, 2, -1)))
.isFalse();
assertThat(survivor1
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock2, 2, -1)))
.isFalse();
final AsyncInvocation lock1FailsReleaseOnOtherVM =
survivor2
.invokeAsync(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock1));
final AsyncInvocation lock2FailsReleaseOnOtherVM =
survivor1
.invokeAsync(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock2));
assertThatThrownBy(lock1FailsReleaseOnOtherVM::get)
.hasRootCauseInstanceOf(LockNotHeldException.class);
assertThatThrownBy(lock2FailsReleaseOnOtherVM::get)
.hasRootCauseInstanceOf(LockNotHeldException.class);
assertThat(survivor1
.invoke(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).lock(lock3, 20_000, -1)))
.isTrue();
final AsyncInvocation lock1SuccessfulRelease =
survivor1
.invokeAsync(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock1));
final AsyncInvocation lock3SuccessfulRelease =
survivor1
.invokeAsync(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock3));
final AsyncInvocation lock2SuccessfulRelease =
survivor2
.invokeAsync(() -> DistributedLockService.getServiceNamed(SERVICE_NAME).unlock(lock2));
lock1SuccessfulRelease.get();
lock2SuccessfulRelease.get();
lock3SuccessfulRelease.get();
}
private static InternalDistributedMember assertIsElderAndGetId() {
DistributionManager distributionManager =
ClusterStartupRule.getCache().getInternalDistributedSystem().getDistributionManager();
await("Wait to be elder")
.untilAsserted(() -> assertThat(distributionManager.isElder()).isTrue());
return distributionManager.getElderId();
}
}