/*
* Licensed to 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.planner;
import com.carrotsearch.hppc.ObjectLongMap;
import io.crate.action.sql.SQLOperations;
import io.crate.data.RowN;
import io.crate.metadata.TableIdent;
import io.crate.plugin.SQLPlugin;
import io.crate.test.integration.CrateDummyClusterServiceUnitTest;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.junit.Test;
import org.mockito.Answers;
import org.mockito.Mockito;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
public class TableStatsServiceTest extends CrateDummyClusterServiceUnitTest {
@Override
protected Collection<Setting<?>> additionalClusterSettings() {
return new SQLPlugin(Settings.EMPTY).getSettings();
}
@Test
public void testSettingsChanges() {
// Initially disabled
TableStatsService statsService = new TableStatsService(
Settings.builder().put(TableStatsService.STATS_SERVICE_REFRESH_INTERVAL_SETTING.getKey(), 0).build(),
THREAD_POOL,
clusterService,
new TableStats(),
mock(SQLOperations.class, Answers.RETURNS_MOCKS.get()));
assertThat(statsService.refreshInterval,
is(TimeValue.timeValueMinutes(0)));
assertThat(statsService.refreshScheduledTask, is(nullValue()));
// Default setting
statsService = new TableStatsService(
Settings.EMPTY,
THREAD_POOL,
clusterService,
new TableStats(),
mock(SQLOperations.class, Answers.RETURNS_MOCKS.get()));
assertThat(statsService.refreshInterval,
is(TableStatsService.STATS_SERVICE_REFRESH_INTERVAL_SETTING.getDefault()));
assertThat(statsService.refreshScheduledTask, is(notNullValue()));
ClusterSettings clusterSettings = clusterService.getClusterSettings();
// Update setting
clusterSettings.applySettings(Settings.builder()
.put(TableStatsService.STATS_SERVICE_REFRESH_INTERVAL_SETTING.getKey(), "10m").build());
assertThat(statsService.refreshInterval, is(TimeValue.timeValueMinutes(10)));
assertThat(statsService.refreshScheduledTask,
is(notNullValue()));
// Disable
clusterSettings.applySettings(Settings.builder()
.put(TableStatsService.STATS_SERVICE_REFRESH_INTERVAL_SETTING.getKey(), 0).build());
assertThat(statsService.refreshInterval, is(TimeValue.timeValueMillis(0)));
assertThat(statsService.refreshScheduledTask,
is(nullValue()));
// Reset setting
clusterSettings.applySettings(Settings.builder().build());
assertThat(statsService.refreshInterval,
is(TableStatsService.STATS_SERVICE_REFRESH_INTERVAL_SETTING.getDefault()));
assertThat(statsService.refreshScheduledTask, is(notNullValue()));
}
@Test
public void testRowsToTableStatConversion() throws InterruptedException, ExecutionException, TimeoutException {
CompletableFuture<ObjectLongMap<TableIdent>> statsFuture = new CompletableFuture<>();
TableStatsService.TableStatsResultReceiver receiver =
new TableStatsService.TableStatsResultReceiver(statsFuture::complete);
receiver.setNextRow(new RowN(new Object[]{1L, "custom", "foo"}));
receiver.setNextRow(new RowN(new Object[]{2L, "doc", "foo"}));
receiver.setNextRow(new RowN(new Object[]{3L, "bar", "foo"}));
receiver.allFinished(false);
ObjectLongMap<TableIdent> stats = statsFuture.get(10, TimeUnit.SECONDS);
assertThat(stats.size(), is(3));
assertThat(stats.get(new TableIdent("bar", "foo")), is(3L));
}
@Test
public void testStatsQueriesCorrectly() throws Throwable {
final SQLOperations sqlOperations = mock(SQLOperations.class);
SQLOperations.SQLDirectExecutor sqlDirectExecutor = mock(SQLOperations.SQLDirectExecutor.class);
when(sqlOperations.createSQLDirectExecutor(
eq("sys"),
eq(TableStatsService.TABLE_STATS),
eq(TableStatsService.STMT),
eq(TableStatsService.DEFAULT_SOFT_LIMIT)))
.thenReturn(sqlDirectExecutor);
TableStatsService statsService = new TableStatsService(
Settings.EMPTY,
THREAD_POOL,
clusterService,
new TableStats(),
sqlOperations
);
statsService.run();
verify(sqlDirectExecutor, times(1)).execute(
any(TableStatsService.TableStatsResultReceiver.class),
eq(Collections.emptyList()));
}
@Test
public void testNoUpdateIfLocalNodeNotAvailable() throws Exception {
final ClusterService clusterService = mock(ClusterService.class);
when(clusterService.localNode()).thenReturn(null);
when(clusterService.getClusterSettings()).thenReturn(this.clusterService.getClusterSettings());
SQLOperations sqlOperations = mock(SQLOperations.class);
SQLOperations.Session session = mock(SQLOperations.Session.class);
when(sqlOperations.createSession(anyString(), anyString(), any(), anyInt())).thenReturn(session);
TableStatsService statsService = new TableStatsService(
Settings.EMPTY,
THREAD_POOL,
clusterService,
new TableStats(),
sqlOperations
);
statsService.run();
Mockito.verify(session, times(0)).sync();
}
}