/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.breaker;
import io.crate.plugin.SQLPlugin;
import io.crate.test.integration.CrateUnitTest;
import org.elasticsearch.common.breaker.CircuitBreaker;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.indices.breaker.CircuitBreakerStats;
import org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import static org.elasticsearch.mock.orig.Mockito.verify;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
public class CrateCircuitBreakerServiceTest extends CrateUnitTest {
private ClusterSettings clusterSettings;
@Before
public void registerSettings() {
SQLPlugin sqlPlugin = new SQLPlugin(Settings.EMPTY);
Set<Setting<?>> settings = new HashSet<>();
settings.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
settings.addAll(sqlPlugin.getSettings());
clusterSettings = new ClusterSettings(Settings.EMPTY, settings);
}
@Test
public void testQueryCircuitBreakerRegistration() throws Exception {
CircuitBreakerService esBreakerService = new HierarchyCircuitBreakerService(
Settings.EMPTY, clusterSettings);
CrateCircuitBreakerService breakerService = new CrateCircuitBreakerService(
Settings.EMPTY, clusterSettings, esBreakerService);
CircuitBreaker breaker = breakerService.getBreaker(CrateCircuitBreakerService.QUERY);
assertThat(breaker, notNullValue());
assertThat(breaker, instanceOf(CircuitBreaker.class));
assertThat(breaker.getName(), is(CrateCircuitBreakerService.QUERY));
}
@Test
public void testQueryCircuitBreakerDynamicSettings() throws Exception {
CircuitBreakerService esBreakerService = new HierarchyCircuitBreakerService(
Settings.EMPTY, clusterSettings);
CrateCircuitBreakerService breakerService = new CrateCircuitBreakerService(
Settings.EMPTY, clusterSettings, esBreakerService);
Settings newSettings = Settings.builder()
.put(CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_OVERHEAD_SETTING.getKey(), 2.0)
.build();
clusterSettings.applySettings(newSettings);
CircuitBreaker breaker = breakerService.getBreaker(CrateCircuitBreakerService.QUERY);
assertThat(breaker, notNullValue());
assertThat(breaker, instanceOf(CircuitBreaker.class));
assertThat(breaker.getOverhead(), is(2.0));
}
@Test
public void testBreakerSettingsAssignment() throws Exception {
Settings settings = Settings.builder()
.put(CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_LIMIT_SETTING.getKey(), "10m")
.put(CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_OVERHEAD_SETTING.getKey(), 1.0)
.build();
CircuitBreakerService esBreakerService = spy(new HierarchyCircuitBreakerService(Settings.EMPTY, clusterSettings));
CrateCircuitBreakerService breakerService = new CrateCircuitBreakerService(settings, clusterSettings, esBreakerService);
CircuitBreaker breaker = breakerService.getBreaker(CrateCircuitBreakerService.QUERY);
assertThat(breaker.getLimit(), is(10_485_760L));
assertThat(breaker.getOverhead(), is(1.0));
Settings newSettings = Settings.builder()
.put(CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_LIMIT_SETTING.getKey(), "100m")
.put(CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_OVERHEAD_SETTING.getKey(), 2.0)
.build();
clusterSettings.applySettings(newSettings);
// expecting 4 times because registerBreaker() is also called from constructor of CrateCircuitBreakerService 3 times
verify(esBreakerService, times(4)).registerBreaker(Matchers.any());
breaker = breakerService.getBreaker(CrateCircuitBreakerService.QUERY);
assertThat(breaker.getLimit(), is(104_857_600L));
assertThat(breaker.getOverhead(), is(2.0));
// updating with same settings should not register a new breaker
clusterSettings.applySettings(newSettings);
verify(esBreakerService, times(4)).registerBreaker(Matchers.any());
}
@Test
public void testBreakingExceptionMessage() throws Exception {
String message = CrateCircuitBreakerService.breakingExceptionMessage("dummy", 1234);
assertThat(message, is(String.format(Locale.ENGLISH, CrateCircuitBreakerService.BREAKING_EXCEPTION_MESSAGE, "dummy", 1234, new ByteSizeValue(1234))));
}
@Test
public void testStats() throws Exception {
CircuitBreakerService esBreakerService = new HierarchyCircuitBreakerService(
Settings.EMPTY, clusterSettings);
CrateCircuitBreakerService breakerService = new CrateCircuitBreakerService(
Settings.EMPTY, clusterSettings, esBreakerService);
CircuitBreakerStats[] stats = breakerService.stats().getAllStats();
assertThat(stats.length, is(7));
CircuitBreakerStats queryBreakerStats = breakerService.stats(CrateCircuitBreakerService.QUERY);
assertThat(queryBreakerStats.getEstimated(), is(0L));
}
}