6 changed files with 259 additions and 28 deletions
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* 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 org.thingsboard.server.dao.timeseries; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
|
||||
|
@Data |
||||
|
@AllArgsConstructor |
||||
|
public class CassandraPartitionCacheKey { |
||||
|
|
||||
|
private EntityId entityId; |
||||
|
private String key; |
||||
|
private long partition; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* 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 org.thingsboard.server.dao.timeseries; |
||||
|
|
||||
|
import com.github.benmanes.caffeine.cache.AsyncLoadingCache; |
||||
|
import com.github.benmanes.caffeine.cache.Caffeine; |
||||
|
|
||||
|
import java.util.concurrent.CompletableFuture; |
||||
|
|
||||
|
public class CassandraTsPartitionsCache { |
||||
|
|
||||
|
private AsyncLoadingCache<CassandraPartitionCacheKey, Boolean> partitionsCache; |
||||
|
|
||||
|
public CassandraTsPartitionsCache(long maxCacheSize) { |
||||
|
this.partitionsCache = Caffeine.newBuilder() |
||||
|
.maximumSize(maxCacheSize) |
||||
|
.buildAsync(key -> { |
||||
|
throw new IllegalStateException("'get' methods calls are not supported!"); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public boolean has(CassandraPartitionCacheKey key) { |
||||
|
return partitionsCache.getIfPresent(key) != null; |
||||
|
} |
||||
|
|
||||
|
public void put(CassandraPartitionCacheKey key) { |
||||
|
partitionsCache.put(key, CompletableFuture.completedFuture(true)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* 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 org.thingsboard.server.dao.nosql; |
||||
|
|
||||
|
import com.datastax.oss.driver.api.core.ConsistencyLevel; |
||||
|
import com.datastax.oss.driver.api.core.cql.BoundStatement; |
||||
|
import com.datastax.oss.driver.api.core.cql.PreparedStatement; |
||||
|
import com.datastax.oss.driver.api.core.cql.Statement; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.junit.runner.RunWith; |
||||
|
import org.mockito.Mock; |
||||
|
import org.mockito.runners.MockitoJUnitRunner; |
||||
|
import org.springframework.core.env.Environment; |
||||
|
import org.springframework.test.util.ReflectionTestUtils; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.cassandra.CassandraCluster; |
||||
|
import org.thingsboard.server.dao.cassandra.guava.GuavaSession; |
||||
|
import org.thingsboard.server.dao.timeseries.CassandraBaseTimeseriesDao; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import static org.mockito.Matchers.any; |
||||
|
import static org.mockito.Matchers.anyString; |
||||
|
import static org.mockito.Mockito.doReturn; |
||||
|
import static org.mockito.Mockito.spy; |
||||
|
import static org.mockito.Mockito.times; |
||||
|
import static org.mockito.Mockito.verify; |
||||
|
import static org.mockito.Mockito.when; |
||||
|
|
||||
|
@RunWith(MockitoJUnitRunner.class) |
||||
|
public class CassandraPartitionsCacheTest { |
||||
|
|
||||
|
private CassandraBaseTimeseriesDao cassandraBaseTimeseriesDao; |
||||
|
|
||||
|
@Mock |
||||
|
private Environment environment; |
||||
|
|
||||
|
@Mock |
||||
|
private CassandraBufferedRateExecutor rateLimiter; |
||||
|
|
||||
|
@Mock |
||||
|
private CassandraCluster cluster; |
||||
|
|
||||
|
@Mock |
||||
|
private GuavaSession session; |
||||
|
|
||||
|
@Mock |
||||
|
private PreparedStatement preparedStatement; |
||||
|
|
||||
|
@Mock |
||||
|
private BoundStatement boundStatement; |
||||
|
|
||||
|
@Before |
||||
|
public void setUp() throws Exception { |
||||
|
when(cluster.getDefaultReadConsistencyLevel()).thenReturn(ConsistencyLevel.ONE); |
||||
|
when(cluster.getDefaultWriteConsistencyLevel()).thenReturn(ConsistencyLevel.ONE); |
||||
|
when(cluster.getSession()).thenReturn(session); |
||||
|
when(session.prepare(anyString())).thenReturn(preparedStatement); |
||||
|
when(preparedStatement.bind()).thenReturn(boundStatement); |
||||
|
|
||||
|
cassandraBaseTimeseriesDao = spy(new CassandraBaseTimeseriesDao()); |
||||
|
|
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "partitioning", "MONTHS"); |
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "partitionsCacheSize", 100000); |
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "systemTtl", 0); |
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "setNullValuesEnabled", false); |
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "environment", environment); |
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "rateLimiter", rateLimiter); |
||||
|
ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "cluster", cluster); |
||||
|
|
||||
|
doReturn(Futures.immediateFuture(null)).when(cassandraBaseTimeseriesDao).getFuture(any(TbResultSetFuture.class), any()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testPartitionSave() throws Exception { |
||||
|
cassandraBaseTimeseriesDao.init(); |
||||
|
|
||||
|
UUID id = UUID.randomUUID(); |
||||
|
TenantId tenantId = new TenantId(id); |
||||
|
long tsKvEntryTs = System.currentTimeMillis(); |
||||
|
|
||||
|
for (int i = 0; i < 50000; i++) { |
||||
|
cassandraBaseTimeseriesDao.savePartition(tenantId, tenantId, tsKvEntryTs, "test" + i, 0); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < 60000; i++) { |
||||
|
cassandraBaseTimeseriesDao.savePartition(tenantId, tenantId, tsKvEntryTs, "test" + i, 0); |
||||
|
} |
||||
|
verify(cassandraBaseTimeseriesDao, times(60000)).executeAsyncWrite(any(TenantId.class), any(Statement.class)); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue