/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed 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 com.hazelcast.spi.impl.operationservice.impl; import com.hazelcast.config.Config; import com.hazelcast.logging.ILogger; import com.hazelcast.spi.BackupAwareOperation; import com.hazelcast.spi.Operation; import com.hazelcast.spi.PartitionAwareOperation; import com.hazelcast.spi.UrgentSystemOperation; import com.hazelcast.spi.properties.HazelcastProperties; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import static com.hazelcast.spi.properties.GroupProperty.BACKPRESSURE_ENABLED; import static com.hazelcast.spi.properties.GroupProperty.BACKPRESSURE_SYNCWINDOW; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class BackpressureRegulatorTest extends HazelcastTestSupport { private static final int SYNC_WINDOW = 100; private ILogger logger; @Before public void setup() { logger = mock(ILogger.class); } @Test public void testBackPressureDisabledByDefault() { Config config = new Config(); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); BackpressureRegulator regulator = new BackpressureRegulator(hazelcastProperties, logger); assertFalse(regulator.isEnabled()); } @Test(expected = IllegalArgumentException.class) public void testConstruction_invalidSyncWindow() { Config config = new Config(); config.setProperty(BACKPRESSURE_ENABLED.getName(), "true"); config.setProperty(BACKPRESSURE_SYNCWINDOW.getName(), "0"); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); new BackpressureRegulator(hazelcastProperties, logger); } @Test public void testConstruction_OneSyncWindow_syncOnEveryCall() { Config config = new Config(); config.setProperty(BACKPRESSURE_ENABLED.getName(), "true"); config.setProperty(BACKPRESSURE_SYNCWINDOW.getName(), "1"); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); BackpressureRegulator regulator = new BackpressureRegulator(hazelcastProperties, logger); for (int k = 0; k < 1000; k++) { PartitionSpecificOperation op = new PartitionSpecificOperation(10); assertTrue(regulator.isSyncForced(op)); } } // ========================== newCallIdSequence ================= @Test public void newCallIdSequence_whenBackPressureEnabled() { Config config = new Config(); config.setProperty(BACKPRESSURE_ENABLED.getName(), "true"); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); BackpressureRegulator backpressureRegulator = new BackpressureRegulator(hazelcastProperties, logger); CallIdSequence callIdSequence = backpressureRegulator.newCallIdSequence(); assertInstanceOf(CallIdSequence.CallIdSequenceWithBackpressure.class, callIdSequence); assertEquals(backpressureRegulator.getMaxConcurrentInvocations(), callIdSequence.getMaxConcurrentInvocations()); } @Test public void newCallIdSequence_whenBackPressureDisabled() { Config config = new Config(); config.setProperty(BACKPRESSURE_ENABLED.getName(), "false"); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); BackpressureRegulator backpressureRegulator = new BackpressureRegulator(hazelcastProperties, logger); CallIdSequence callIdSequence = backpressureRegulator.newCallIdSequence(); assertInstanceOf(CallIdSequence.CallIdSequenceWithoutBackpressure.class, callIdSequence); } // ========================== isSyncForced ================= @Test public void isSyncForced_whenUrgentOperation_thenFalse() { BackpressureRegulator regulator = newEnabledBackPressureService(); UrgentOperation operation = new UrgentOperation(); operation.setPartitionId(1); boolean result = regulator.isSyncForced(operation); assertFalse(result); } @Test public void isSyncForced_whenDisabled_thenFalse() { BackpressureRegulator regulator = newDisabledBackPressureService(); PartitionSpecificOperation op = new PartitionSpecificOperation(10); int oldSyncDelay = regulator.syncCountDown(); boolean result = regulator.isSyncForced(op); assertFalse(result); assertEquals(oldSyncDelay, regulator.syncCountDown()); } @Test public void isSyncForced_whenNoAsyncBackups_thenFalse() { BackpressureRegulator regulator = newEnabledBackPressureService(); PartitionSpecificOperation op = new PartitionSpecificOperation(10) { @Override public int getAsyncBackupCount() { return 0; } }; int oldSyncDelay = regulator.syncCountDown(); boolean result = regulator.isSyncForced(op); assertFalse(result); assertEquals(oldSyncDelay, regulator.syncCountDown()); } @Test public void isSyncForced_whenPartitionSpecific() { BackpressureRegulator regulator = newEnabledBackPressureService(); BackupAwareOperation op = new PartitionSpecificOperation(10); for (int iteration = 0; iteration < 10; iteration++) { int initialSyncDelay = regulator.syncCountDown(); int remainingSyncDelay = initialSyncDelay - 1; for (int k = 0; k < initialSyncDelay - 1; k++) { boolean result = regulator.isSyncForced(op); assertFalse("no sync force expected", result); int syncDelay = regulator.syncCountDown(); assertEquals(remainingSyncDelay, syncDelay); remainingSyncDelay--; } boolean result = regulator.isSyncForced(op); assertTrue("sync force expected", result); int syncDelay = regulator.syncCountDown(); assertValidSyncDelay(syncDelay); } } private void assertValidSyncDelay(int synDelay) { assertTrue("syncDelayCounter is " + synDelay, synDelay >= (1 - BackpressureRegulator.RANGE) * SYNC_WINDOW); assertTrue("syncDelayCounter is " + synDelay, synDelay <= (1 + BackpressureRegulator.RANGE) * SYNC_WINDOW); } private class UrgentOperation extends Operation implements UrgentSystemOperation, BackupAwareOperation { @Override public void run() throws Exception { } @Override public boolean shouldBackup() { return false; } @Override public int getSyncBackupCount() { return 0; } @Override public int getAsyncBackupCount() { return 1; } @Override public Operation getBackupOperation() { return null; } } private class PartitionSpecificOperation extends Operation implements PartitionAwareOperation, BackupAwareOperation { public PartitionSpecificOperation(int partitionId) { setPartitionId(partitionId); } @Override public void run() throws Exception { } @Override public boolean shouldBackup() { return true; } @Override public int getSyncBackupCount() { return 0; } @Override public int getAsyncBackupCount() { return 1; } @Override public Operation getBackupOperation() { return null; } } private class GenericOperation extends Operation implements BackupAwareOperation { public GenericOperation() { setPartitionId(-1); } @Override public void run() throws Exception { } @Override public boolean shouldBackup() { return true; } @Override public int getSyncBackupCount() { return 0; } @Override public int getAsyncBackupCount() { return 1; } @Override public Operation getBackupOperation() { return null; } } private BackpressureRegulator newEnabledBackPressureService() { Config config = new Config(); config.setProperty(BACKPRESSURE_ENABLED.getName(), "true"); config.setProperty(BACKPRESSURE_SYNCWINDOW.getName(), String.valueOf(SYNC_WINDOW)); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); return new BackpressureRegulator(hazelcastProperties, logger); } private BackpressureRegulator newDisabledBackPressureService() { Config config = new Config(); config.setProperty(BACKPRESSURE_ENABLED.getName(), "false"); HazelcastProperties hazelcastProperties = new HazelcastProperties(config); return new BackpressureRegulator(hazelcastProperties, logger); } }