From 04f7b2a525f75ee4e62b7f8c388f74264a8af603 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Tue, 23 Jun 2020 20:35:47 +0100
Subject: [PATCH 01/31] add lazy initialization; change API to facilitate it
---
.../Core/ClientTimeoutTests.cs | 2 +-
.../Core/ConnectionSimulacronTests.cs | 20 +-
.../Core/HeartbeatTests.cs | 2 +-
.../Core/PoolShortTests.cs | 8 +-
.../Core/SessionStateTests.cs | 2 +-
.../Core/SessionTests.cs | 31 +-
.../Core/SpeculativeExecutionLongTests.cs | 2 +-
src/Cassandra.Tests/BuilderTests.cs | 2 +-
.../Connections/HostConnectionPoolTests.cs | 2 +-
.../DataStax/Insights/InsightsClientTests.cs | 10 +-
.../Mapping/Linq/LinqCreateTableUnitTests.cs | 6 +-
src/Cassandra.Tests/RequestHandlerTests.cs | 12 +-
.../Requests/PrepareHandlerTests.cs | 12 +-
src/Cassandra.Tests/SessionTests.cs | 2 +-
src/Cassandra/Builder.cs | 4 +-
src/Cassandra/Cluster.cs | 387 +++++++++---------
.../Connections/Control/ControlConnection.cs | 18 +-
.../Control/ControlConnectionFactory.cs | 5 +-
.../Control/IControlConnectionFactory.cs | 3 +-
.../Connections/HostConnectionPool.cs | 6 +-
src/Cassandra/Data/Linq/CqlQueryBase.cs | 5 +
src/Cassandra/Data/Linq/SessionExtensions.cs | 31 +-
src/Cassandra/Data/Linq/Table.cs | 11 +-
.../DataStax/Insights/IInsightsClient.cs | 2 +-
.../Insights/IInsightsSupportVerifier.cs | 5 +-
.../InfoProviders/IInsightsInfoProvider.cs | 2 +-
.../AuthProviderInfoProvider.cs | 3 +-
.../ConfigAntiPatternsInfoProvider.cs | 6 +-
.../StartupMessage/DataCentersInfoProvider.cs | 6 +-
.../StartupMessage/DriverInfoProvider.cs | 3 +-
.../ExecutionProfileInfoProvider.cs | 3 +-
.../StartupMessage/HostnameInfoProvider.cs | 2 +-
.../OtherOptionsInfoProvider.cs | 2 +-
.../StartupMessage/PlatformInfoProvider.cs | 2 +-
.../PoolSizeByHostDistanceInfoProvider.cs | 6 +-
.../ReconnectionPolicyInfoProvider.cs | 2 +-
.../StatusMessage/NodeStatusInfoProvider.cs | 5 +-
.../DataStax/Insights/InsightsClient.cs | 34 +-
.../Insights/InsightsSupportVerifier.cs | 10 +-
.../IInsightsMessageFactory.cs | 2 +-
.../InsightsStartupMessageFactory.cs | 36 +-
.../InsightsStatusMessageFactory.cs | 10 +-
src/Cassandra/Extensions.cs | 31 +-
src/Cassandra/ICluster.cs | 81 +---
src/Cassandra/IMetadataQueryProvider.cs | 6 +-
src/Cassandra/ISession.cs | 10 -
src/Cassandra/Metadata.cs | 78 ++--
.../ConstantSpeculativeExecutionPolicy.cs | 2 +-
.../Policies/DCAwareRoundRobinPolicy.cs | 27 +-
.../Policies/DefaultLoadBalancingPolicy.cs | 14 +-
.../Policies/ILoadBalancingPolicy.cs | 7 +-
.../Policies/ILocalDatacenterProvider.cs | 2 +-
.../Policies/ISpeculativeExecutionPolicy.cs | 2 +-
.../Policies/LocalDatacenterProvider.cs | 31 +-
.../Policies/NoSpeculativeExecutionPolicy.cs | 2 +-
.../Policies/RetryLoadBalancingPolicy.cs | 8 +-
src/Cassandra/Policies/RoundRobinPolicy.cs | 20 +-
src/Cassandra/Policies/TokenAwarePolicy.cs | 17 +-
.../ProtocolEvents/ProtocolEventDebouncer.cs | 10 +-
src/Cassandra/ProtocolVersion.cs | 8 +
src/Cassandra/QueryTrace.cs | 9 +-
src/Cassandra/Requests/IPrepareHandler.cs | 4 +-
src/Cassandra/Requests/IRequestHandler.cs | 7 +
.../Requests/IRequestHandlerFactory.cs | 13 +-
src/Cassandra/Requests/PrepareHandler.cs | 21 +-
src/Cassandra/Requests/ReprepareHandler.cs | 3 +-
src/Cassandra/Requests/RequestExecution.cs | 27 +-
src/Cassandra/Requests/RequestHandler.cs | 151 ++++++-
.../Requests/RequestHandlerFactory.cs | 18 +-
src/Cassandra/SchemaParser.cs | 6 +-
src/Cassandra/Session.cs | 127 +++---
.../SessionManagement/IInternalCluster.cs | 9 +-
.../SessionManagement/IInternalSession.cs | 5 +-
.../SessionManagement/ISessionFactory.cs | 7 +-
.../SessionManagement/SessionFactory.cs | 10 +-
src/Cassandra/SessionState.cs | 4 +-
src/Cassandra/UdtMappingDefinitions.cs | 22 +-
77 files changed, 869 insertions(+), 654 deletions(-)
diff --git a/src/Cassandra.IntegrationTests/Core/ClientTimeoutTests.cs b/src/Cassandra.IntegrationTests/Core/ClientTimeoutTests.cs
index a184ca40c..c9d71fb58 100644
--- a/src/Cassandra.IntegrationTests/Core/ClientTimeoutTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/ClientTimeoutTests.cs
@@ -206,7 +206,7 @@ public void Should_Use_Statement_ReadTimeout()
var queryOptions = new QueryOptions().SetRetryOnTimeout(false);
var builder = ClusterBuilder().AddContactPoint(_testCluster.InitialContactPoint)
.WithSocketOptions(socketOptions)
- .WithPoolingOptions(PoolingOptions.Create().SetHeartBeatInterval(0))
+ .WithPoolingOptions(PoolingOptions.Get().SetHeartBeatInterval(0))
.WithQueryTimeout(Timeout.Infinite)
.WithQueryOptions(queryOptions);
using (var cluster = builder.Build())
diff --git a/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs b/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs
index 89fcf5d2f..a6ed492eb 100644
--- a/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs
@@ -51,8 +51,9 @@ public async Task Should_ThrowOperationTimedOut_When_ServerAppliesTcpBackPressur
.SetStreamMode(streamMode)
.SetDefunctReadTimeoutThreshold(int.MaxValue)));
+ var metadata = await Session.Cluster.GetMetadataAsync().ConfigureAwait(false);
var maxRequestsPerConnection = Session.Cluster.Configuration
- .GetOrCreatePoolingOptions(Session.Cluster.Metadata.ControlConnection.ProtocolVersion)
+ .GetPoolingOptions(metadata.ProtocolVersion)
.GetMaxRequestsPerConnection();
var tenKbBuffer = new byte[10240];
@@ -60,7 +61,7 @@ public async Task Should_ThrowOperationTimedOut_When_ServerAppliesTcpBackPressur
// send number of requests = max pending
var requests =
- Enumerable.Repeat(0, maxRequestsPerConnection * Session.Cluster.AllHosts().Count)
+ Enumerable.Repeat(0, maxRequestsPerConnection * metadata.AllHosts().Count)
.Select(i => Session.ExecuteAsync(new SimpleStatement("INSERT INTO table1 (id) VALUES (?)", tenKbBuffer))).ToList();
var taskAll = Task.WhenAll(requests);
@@ -88,6 +89,7 @@ public async Task Should_ThrowOperationTimedOut_When_ServerAppliesTcpBackPressur
[Test]
public async Task Should_RetryOnNextNodes_When_ANodeIsPaused(bool streamMode)
{
+ var metadata = await Session.Cluster.GetMetadataAsync().ConfigureAwait(false);
var pausedNode = TestCluster.GetNode(2);
SetupNewSession(b =>
@@ -103,7 +105,7 @@ public async Task Should_RetryOnNextNodes_When_ANodeIsPaused(bool streamMode)
var maxRequestsPerConnection =
Session.Cluster.Configuration
- .GetOrCreatePoolingOptions(Session.Cluster.Metadata.ControlConnection.ProtocolVersion)
+ .GetPoolingOptions(metadata.ProtocolVersion)
.GetMaxRequestsPerConnection();
var tenKbBuffer = new byte[10240];
@@ -112,7 +114,7 @@ public async Task Should_RetryOnNextNodes_When_ANodeIsPaused(bool streamMode)
// send number of requests = max pending
var requests =
- Enumerable.Repeat(0, maxRequestsPerConnection * Session.Cluster.AllHosts().Count)
+ Enumerable.Repeat(0, maxRequestsPerConnection * metadata.AllHosts().Count)
.Select(i => Session.ExecuteAsync(new SimpleStatement("INSERT INTO table1 (id) VALUES (?)", tenKbBuffer))).ToList();
var pools = InternalSession.GetPools().ToList();
@@ -146,6 +148,7 @@ public async Task Should_RetryOnNextNodes_When_ANodeIsPaused(bool streamMode)
[Test]
public async Task Should_ContinueRoutingTrafficToNonPausedNodes_When_ANodeIsPaused(bool streamMode)
{
+ var metadata = await Session.Cluster.GetMetadataAsync().ConfigureAwait(false);
var pausedNode = TestCluster.GetNode(2);
const string profileName = "running-nodes";
@@ -167,7 +170,7 @@ public async Task Should_ContinueRoutingTrafficToNonPausedNodes_When_ANodeIsPaus
var maxRequestsPerConnection =
Session.Cluster.Configuration
- .GetOrCreatePoolingOptions(Session.Cluster.Metadata.ControlConnection.ProtocolVersion)
+ .GetPoolingOptions(metadata.ProtocolVersion)
.GetMaxRequestsPerConnection();
var tenKbBuffer = new byte[10240];
@@ -176,7 +179,7 @@ public async Task Should_ContinueRoutingTrafficToNonPausedNodes_When_ANodeIsPaus
// send number of requests = max pending
var requests =
- Enumerable.Repeat(0, maxRequestsPerConnection * Session.Cluster.AllHosts().Count)
+ Enumerable.Repeat(0, maxRequestsPerConnection * metadata.AllHosts().Count)
.Select(i => Session.ExecuteAsync(new SimpleStatement("INSERT INTO table1 (id) VALUES (?)", tenKbBuffer))).ToList();
try
@@ -233,6 +236,7 @@ public async Task Should_ContinueRoutingTrafficToNonPausedNodes_When_ANodeIsPaus
[Test]
public async Task Should_KeepOperationsInWriteQueue_When_ServerAppliesTcpBackPressure(bool streamMode)
{
+ var metadata = await Session.Cluster.GetMetadataAsync().ConfigureAwait(false);
SetupNewSession(b =>
b.WithPoolingOptions(
new PoolingOptions()
@@ -243,7 +247,7 @@ public async Task Should_KeepOperationsInWriteQueue_When_ServerAppliesTcpBackPre
.SetStreamMode(streamMode)));
var maxRequestsPerConnection = Session.Cluster.Configuration
- .GetOrCreatePoolingOptions(Session.Cluster.Metadata.ControlConnection.ProtocolVersion)
+ .GetPoolingOptions(metadata.ProtocolVersion)
.GetMaxRequestsPerConnection();
var tenKbBuffer = new byte[10240];
@@ -296,7 +300,7 @@ public async Task Should_KeepOperationsInWriteQueue_When_ServerAppliesTcpBackPre
Assert.Greater(moreFailedRequests.Count, 1);
Assert.AreEqual(moreRequests.Count, moreFailedRequests.Count);
- Assert.GreaterOrEqual(connections.Sum(c => c.InFlight), maxRequestsPerConnection * Session.Cluster.AllHosts().Count);
+ Assert.GreaterOrEqual(connections.Sum(c => c.InFlight), maxRequestsPerConnection * metadata.AllHosts().Count);
// ReSharper disable once PossibleNullReferenceException
Assert.IsTrue(moreFailedRequests.All(t => t.IsFaulted && ((NoHostAvailableException)t.Exception.InnerException).Errors.All(e => e.Value is BusyPoolException)));
diff --git a/src/Cassandra.IntegrationTests/Core/HeartbeatTests.cs b/src/Cassandra.IntegrationTests/Core/HeartbeatTests.cs
index 1c18b38fc..95e0650c6 100644
--- a/src/Cassandra.IntegrationTests/Core/HeartbeatTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/HeartbeatTests.cs
@@ -49,7 +49,7 @@ public void OneTimeTearDown()
public async Task Connection_Should_Send_Options_Requests_For_Heartbeats(bool executeQuery)
{
var builder = ClusterBuilder()
- .WithPoolingOptions(PoolingOptions.Create().SetHeartBeatInterval(4000))
+ .WithPoolingOptions(PoolingOptions.Get().SetHeartBeatInterval(4000))
.AddContactPoint(_testCluster.InitialContactPoint);
using (var cluster = builder.Build())
diff --git a/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs b/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs
index 51d4d1932..3cc51c4dd 100644
--- a/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs
@@ -256,7 +256,7 @@ public void Connect_With_Ssl_Test()
[TestCase(ProtocolVersion.V2, 2)]
public async Task PoolingOptions_Create_Based_On_Protocol(ProtocolVersion protocolVersion, int coreConnectionLength)
{
- var options1 = PoolingOptions.Create(protocolVersion);
+ var options1 = PoolingOptions.Get(protocolVersion);
using (var sCluster = SimulacronCluster.CreateNew(new SimulacronOptions()))
using (var cluster = ClusterBuilder()
.AddContactPoint(sCluster.InitialContactPoint)
@@ -284,7 +284,7 @@ await TestHelper.RetryAssertAsync(async () =>
public async Task Should_Create_Core_Connections_To_Hosts_In_Local_Dc_When_Warmup_Is_Enabled()
{
const int nodeLength = 4;
- var poolingOptions = PoolingOptions.Create().SetCoreConnectionsPerHost(HostDistance.Local, 5);
+ var poolingOptions = PoolingOptions.Get().SetCoreConnectionsPerHost(HostDistance.Local, 5);
// Use multiple DCs: 4 nodes in first DC and 3 nodes in second DC
using (var testCluster = SimulacronCluster.CreateNew(new SimulacronOptions { Nodes = $"{nodeLength},3" }))
@@ -430,7 +430,7 @@ public async Task Should_Use_Next_Host_When_First_Host_Is_Busy()
const int maxRequestsPerConnection = 100;
var builder = ClusterBuilder()
.WithPoolingOptions(
- PoolingOptions.Create()
+ PoolingOptions.Get()
.SetCoreConnectionsPerHost(HostDistance.Local, connectionLength)
.SetMaxConnectionsPerHost(HostDistance.Local, connectionLength)
.SetHeartBeatInterval(0)
@@ -480,7 +480,7 @@ public async Task Should_Throw_NoHostAvailableException_When_All_Host_Are_Busy()
var builder = ClusterBuilder()
.WithPoolingOptions(
- PoolingOptions.Create()
+ PoolingOptions.Get()
.SetCoreConnectionsPerHost(HostDistance.Local, connectionLength)
.SetMaxConnectionsPerHost(HostDistance.Local, connectionLength)
.SetHeartBeatInterval(0)
diff --git a/src/Cassandra.IntegrationTests/Core/SessionStateTests.cs b/src/Cassandra.IntegrationTests/Core/SessionStateTests.cs
index 0571f6542..fbe14638f 100644
--- a/src/Cassandra.IntegrationTests/Core/SessionStateTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/SessionStateTests.cs
@@ -54,7 +54,7 @@ public void OneTimeTearDown()
[Test]
public async Task Session_GetState_Should_Return_A_Snapshot_Of_The_Pools_State()
{
- var poolingOptions = PoolingOptions.Create().SetCoreConnectionsPerHost(HostDistance.Local, 2);
+ var poolingOptions = PoolingOptions.Get().SetCoreConnectionsPerHost(HostDistance.Local, 2);
using (var cluster = ClusterBuilder()
.AddContactPoint(_testCluster.InitialContactPoint)
.WithPoolingOptions(poolingOptions)
diff --git a/src/Cassandra.IntegrationTests/Core/SessionTests.cs b/src/Cassandra.IntegrationTests/Core/SessionTests.cs
index a2b1e8c01..0387c5b64 100644
--- a/src/Cassandra.IntegrationTests/Core/SessionTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/SessionTests.cs
@@ -171,7 +171,7 @@ public void Session_Keyspace_Create_Case_Sensitive()
}
[Test]
- public void Should_Create_The_Right_Amount_Of_Connections()
+ public async Task Should_Create_The_Right_Amount_Of_Connections()
{
var localCluster1 = GetNewTemporaryCluster(
builder => builder
@@ -180,7 +180,8 @@ public void Should_Create_The_Right_Amount_Of_Connections()
.SetCoreConnectionsPerHost(HostDistance.Local, 3)));
var localSession1 = (IInternalSession)localCluster1.Connect();
- var hosts1 = localCluster1.AllHosts().ToList();
+ var metadata1 = await localCluster1.GetMetadataAsync().ConfigureAwait(false);
+ var hosts1 = metadata1.AllHosts().ToList();
Assert.AreEqual(3, hosts1.Count);
//Execute multiple times a query on the newly created keyspace
for (var i = 0; i < 12; i++)
@@ -188,9 +189,9 @@ public void Should_Create_The_Right_Amount_Of_Connections()
localSession1.Execute("SELECT * FROM system.local");
}
- Thread.Sleep(2000);
- var pool11 = localSession1.GetOrCreateConnectionPool(hosts1[0], HostDistance.Local);
- var pool12 = localSession1.GetOrCreateConnectionPool(hosts1[1], HostDistance.Local);
+ await Task.Delay(2000).ConfigureAwait(false);
+ var pool11 = await localSession1.GetOrCreateConnectionPool(hosts1[0], HostDistance.Local).ConfigureAwait(false);
+ var pool12 = await localSession1.GetOrCreateConnectionPool(hosts1[1], HostDistance.Local).ConfigureAwait(false);
Assert.That(pool11.OpenConnections, Is.EqualTo(3));
Assert.That(pool12.OpenConnections, Is.EqualTo(3));
@@ -199,8 +200,9 @@ public void Should_Create_The_Right_Amount_Of_Connections()
.WithPoolingOptions(new PoolingOptions().SetCoreConnectionsPerHost(HostDistance.Local, 1))
.Build())
{
+ var metadata2 = await localCluster2.GetMetadataAsync().ConfigureAwait(false);
var localSession2 = (IInternalSession)localCluster2.Connect();
- var hosts2 = localCluster2.AllHosts().ToList();
+ var hosts2 = metadata2.AllHosts().ToList();
Assert.AreEqual(3, hosts2.Count);
//Execute multiple times a query on the newly created keyspace
for (var i = 0; i < 6; i++)
@@ -208,9 +210,9 @@ public void Should_Create_The_Right_Amount_Of_Connections()
localSession2.Execute("SELECT * FROM system.local");
}
- Thread.Sleep(2000);
- var pool21 = localSession2.GetOrCreateConnectionPool(hosts2[0], HostDistance.Local);
- var pool22 = localSession2.GetOrCreateConnectionPool(hosts2[1], HostDistance.Local);
+ await Task.Delay(2000).ConfigureAwait(false);
+ var pool21 = await localSession2.GetOrCreateConnectionPool(hosts2[0], HostDistance.Local).ConfigureAwait(false);
+ var pool22 = await localSession2.GetOrCreateConnectionPool(hosts2[1], HostDistance.Local).ConfigureAwait(false);
Assert.That(pool21.OpenConnections, Is.EqualTo(1));
Assert.That(pool22.OpenConnections, Is.EqualTo(1));
}
@@ -237,14 +239,15 @@ public async Task Session_With_Host_Changing_Distance()
var counter = 0;
using (var localCluster = builder.Build())
{
- var localSession = (IInternalSession)localCluster.Connect();
- var remoteHost = localCluster.AllHosts().First(h => TestHelper.GetLastAddressByte(h) == 2);
+ var metadata = await localCluster.GetMetadataAsync().ConfigureAwait(false);
+ var localSession = (IInternalSession) await localCluster.ConnectAsync().ConfigureAwait(false);
+ var remoteHost = metadata.AllHosts().First(h => TestHelper.GetLastAddressByte(h) == 2);
var stopWatch = new Stopwatch();
var distanceReset = 0;
TestHelper.Invoke(() => localSession.Execute("SELECT key FROM system.local"), 10);
- var hosts = localCluster.AllHosts().ToArray();
- var pool1 = localSession.GetOrCreateConnectionPool(hosts[0], HostDistance.Local);
- var pool2 = localSession.GetOrCreateConnectionPool(hosts[1], HostDistance.Local);
+ var hosts = metadata.AllHosts().ToArray();
+ var pool1 = await localSession.GetOrCreateConnectionPool(hosts[0], HostDistance.Local).ConfigureAwait(false);
+ var pool2 = await localSession.GetOrCreateConnectionPool(hosts[1], HostDistance.Local).ConfigureAwait(false);
var tcs = new TaskCompletionSource();
tcs.SetResult(null);
var completedTask = tcs.Task;
diff --git a/src/Cassandra.IntegrationTests/Core/SpeculativeExecutionLongTests.cs b/src/Cassandra.IntegrationTests/Core/SpeculativeExecutionLongTests.cs
index 209fbd64d..b46fc1b77 100644
--- a/src/Cassandra.IntegrationTests/Core/SpeculativeExecutionLongTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/SpeculativeExecutionLongTests.cs
@@ -90,7 +90,7 @@ public void SpeculativeExecution_Pause_Using_All_Stream_Ids()
Cluster.MaxProtocolVersion = 2;
try
{
- var pooling = PoolingOptions.Create();
+ var pooling = PoolingOptions.Get();
var session = GetSession(new ConstantSpeculativeExecutionPolicy(50L, 1), true, null, pooling);
const int pauseThreshold = 140 * 2;
var tasks = new List>();
diff --git a/src/Cassandra.Tests/BuilderTests.cs b/src/Cassandra.Tests/BuilderTests.cs
index 115d39438..0fb48e36d 100644
--- a/src/Cassandra.Tests/BuilderTests.cs
+++ b/src/Cassandra.Tests/BuilderTests.cs
@@ -165,7 +165,7 @@ public void WithMaxProtocolVersion_Validates_Greater_Than_Zero()
public void PoolingOptions_Create_Based_On_Protocol_Version(ProtocolVersion protocolVersion,
int coreConnections, int maxConnections)
{
- var options1 = PoolingOptions.Create(protocolVersion);
+ var options1 = PoolingOptions.Get(protocolVersion);
var cluster1 = Cluster.Builder()
.AddContactPoint("::1")
.WithPoolingOptions(options1)
diff --git a/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs b/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs
index c8e521c57..eccc8e26e 100644
--- a/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs
+++ b/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs
@@ -104,7 +104,7 @@ private IHostConnectionPool CreatePool(IEndPointResolver res = null)
new DefaultRetryPolicy(),
NoSpeculativeExecutionPolicy.Instance,
new AtomicMonotonicTimestampGenerator()),
- PoolingOptions = PoolingOptions.Create(ProtocolVersion.V4).SetCoreConnectionsPerHost(HostDistance.Local, 2)
+ PoolingOptions = PoolingOptions.Get(ProtocolVersion.V4).SetCoreConnectionsPerHost(HostDistance.Local, 2)
}.Build();
var pool = new HostConnectionPool(
diff --git a/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs b/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs
index 3b2509494..8d7da5be8 100644
--- a/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs
+++ b/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs
@@ -73,7 +73,7 @@ public void Should_LogFiveTimes_When_ThereAreMoreThanFiveErrorsOnStartupMessageS
It.IsAny());
Mock.Get(cluster.Metadata.ControlConnection).Setup(mockExpression).ReturnsAsync((Response)null);
- target.Init();
+ target.InitializeAsync();
TestHelper.RetryAssert(
() =>
@@ -113,7 +113,7 @@ public void Should_ResetErrorCounterForLogging_When_ThereSendMessageIsSuccessful
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.ReturnsAsync((Response)null);
- target.Init();
+ target.InitializeAsync();
TestHelper.RetryAssert(
() =>
@@ -257,7 +257,7 @@ public void Should_InvokeRpcCallCorrectlyAndImmediately_When_SendStartupMessageI
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.Callback((query, opts) => { queryProtocolOptions.Enqueue(opts); });
- target.Init();
+ target.InitializeAsync();
TestHelper.RetryAssert(
() => { Assert.GreaterOrEqual(queryProtocolOptions.Count, 1); }, 10, 50);
@@ -381,7 +381,7 @@ public void Should_InvokeRpcCallCorrectlyAndImmediatelyWithExecutionProfiles_Whe
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.Callback((query, opts) => { queryProtocolOptions.Enqueue(opts); });
- target.Init();
+ target.InitializeAsync();
TestHelper.RetryAssert(
() => { Assert.GreaterOrEqual(queryProtocolOptions.Count, 1); }, 10, 50);
@@ -418,7 +418,7 @@ public void Should_InvokeRpcCallCorrectlyAndPeriodically_When_SendStatusMessageI
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.Callback((query, opts) => { queryProtocolOptions.Enqueue(opts); });
- target.Init();
+ target.InitializeAsync();
TestHelper.RetryAssert(() => { Assert.GreaterOrEqual(queryProtocolOptions.Count, 5); }, 5, 400);
queryProtocolOptions.TryDequeue(out var result); // ignore startup message
diff --git a/src/Cassandra.Tests/Mapping/Linq/LinqCreateTableUnitTests.cs b/src/Cassandra.Tests/Mapping/Linq/LinqCreateTableUnitTests.cs
index 0c26462f6..fed8a868d 100644
--- a/src/Cassandra.Tests/Mapping/Linq/LinqCreateTableUnitTests.cs
+++ b/src/Cassandra.Tests/Mapping/Linq/LinqCreateTableUnitTests.cs
@@ -585,11 +585,9 @@ private static Mock GetSessionMock(ISerializerManager serializer = nul
serializer = new SerializerManager(ProtocolVersion.MaxSupported);
}
var sessionMock = new Mock(MockBehavior.Strict);
- var config = new Configuration();
- var metadata = new Metadata(config);
var ccMock = new Mock(MockBehavior.Strict);
- ccMock.Setup(cc => cc.Serializer).Returns(serializer);
- metadata.ControlConnection = ccMock.Object;
+ var config = new Configuration();
+ var metadata = new Metadata(config, serializer, ccMock.Object);
var clusterMock = new Mock();
clusterMock.Setup(c => c.Metadata).Returns(metadata);
clusterMock.Setup(c => c.Configuration).Returns(config);
diff --git a/src/Cassandra.Tests/RequestHandlerTests.cs b/src/Cassandra.Tests/RequestHandlerTests.cs
index 8cd0b84d8..658d41814 100644
--- a/src/Cassandra.Tests/RequestHandlerTests.cs
+++ b/src/Cassandra.Tests/RequestHandlerTests.cs
@@ -308,7 +308,7 @@ public void GetRequest_With_Timestamp_Generator_Empty_Value()
Cassandra.Policies.DefaultLoadBalancingPolicy, Cassandra.Policies.DefaultReconnectionPolicy,
Cassandra.Policies.DefaultRetryPolicy, Cassandra.Policies.DefaultSpeculativeExecutionPolicy,
new NoTimestampGenerator());
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Create());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Get());
var request = RequestHandler.GetRequest(
statement, RequestHandlerTests.Serializer, config.DefaultRequestOptions);
@@ -341,7 +341,7 @@ public void GetRequest_With_Timestamp_Generator_Empty_Value_With_Statement_Times
Cassandra.Policies.DefaultLoadBalancingPolicy, Cassandra.Policies.DefaultReconnectionPolicy,
Cassandra.Policies.DefaultRetryPolicy, Cassandra.Policies.DefaultSpeculativeExecutionPolicy,
new NoTimestampGenerator());
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Create());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Get());
var request = RequestHandler.GetRequest(statement, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -372,7 +372,7 @@ public void GetRequest_Batch_With_64K_Queries()
batch.Add(new SimpleStatement("QUERY"));
}
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Create());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Get());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -390,7 +390,7 @@ public void GetRequest_Batch_With_Timestamp_Generator()
// To microsecond precision
startDate = startDate.Subtract(TimeSpan.FromTicks(startDate.Ticks % 10));
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Create());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Get());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -426,7 +426,7 @@ public void GetRequest_Batch_With_Empty_Timestamp_Generator()
Cassandra.Policies.DefaultRetryPolicy, Cassandra.Policies.DefaultSpeculativeExecutionPolicy,
new NoTimestampGenerator());
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Create());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Get());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -453,7 +453,7 @@ public void GetRequest_Batch_With_Provided_Timestamp()
providedTimestamp = providedTimestamp.Subtract(TimeSpan.FromTicks(providedTimestamp.Ticks % 10));
batch.SetTimestamp(providedTimestamp);
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Create());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Get());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
diff --git a/src/Cassandra.Tests/Requests/PrepareHandlerTests.cs b/src/Cassandra.Tests/Requests/PrepareHandlerTests.cs
index f1fc60888..7151faf17 100644
--- a/src/Cassandra.Tests/Requests/PrepareHandlerTests.cs
+++ b/src/Cassandra.Tests/Requests/PrepareHandlerTests.cs
@@ -85,7 +85,7 @@ public async Task Should_NotSendRequestToSecondHost_When_SecondHostDoesntHavePoo
var distanceCount = Interlocked.Read(ref lbpCluster.DistanceCount);
var request = new PrepareRequest(_serializer, "TEST", null, null);
- await mockResult.PrepareHandler.Prepare(
+ await mockResult.PrepareHandler.PrepareAsync(
request,
mockResult.Session,
queryPlan.GetEnumerator()).ConfigureAwait(false);
@@ -156,7 +156,7 @@ public async Task Should_NotSendRequestToSecondHost_When_SecondHostPoolDoesNotHa
var distanceCount = Interlocked.Read(ref lbpCluster.DistanceCount);
var request = new PrepareRequest(_serializer, "TEST", null, null);
- await mockResult.PrepareHandler.Prepare(
+ await mockResult.PrepareHandler.PrepareAsync(
request,
mockResult.Session,
queryPlan.GetEnumerator()).ConfigureAwait(false);
@@ -228,7 +228,7 @@ public async Task Should_SendRequestToAllHosts_When_AllHostsHaveConnections()
var distanceCount = Interlocked.Read(ref lbpCluster.DistanceCount);
var request = new PrepareRequest(_serializer, "TEST", null, null);
- await mockResult.PrepareHandler.Prepare(
+ await mockResult.PrepareHandler.PrepareAsync(
request,
mockResult.Session,
queryPlan.GetEnumerator()).ConfigureAwait(false);
@@ -299,7 +299,7 @@ public async Task Should_SendRequestToAllHosts_When_AllHostsHaveConnectionsButFi
var distanceCount = Interlocked.Read(ref lbpCluster.DistanceCount);
var request = new PrepareRequest(_serializer, "TEST", null, null);
- await mockResult.PrepareHandler.Prepare(
+ await mockResult.PrepareHandler.PrepareAsync(
request,
mockResult.Session,
queryPlan.GetEnumerator()).ConfigureAwait(false);
@@ -370,7 +370,7 @@ public async Task Should_SendRequestToAllHosts_When_AllHostsHaveConnectionsButFi
var distanceCount = Interlocked.Read(ref lbpCluster.DistanceCount);
var request = new PrepareRequest(_serializer, "TEST", null, null);
- await mockResult.PrepareHandler.Prepare(
+ await mockResult.PrepareHandler.PrepareAsync(
request,
mockResult.Session,
queryPlan.GetEnumerator()).ConfigureAwait(false);
@@ -442,7 +442,7 @@ public async Task Should_SendRequestToFirstHostOnly_When_PrepareOnAllHostsIsFals
var distanceCount = Interlocked.Read(ref lbpCluster.DistanceCount);
var request = new PrepareRequest(_serializer, "TEST", null, null);
- await mockResult.PrepareHandler.Prepare(
+ await mockResult.PrepareHandler.PrepareAsync(
request,
mockResult.Session,
queryPlan.GetEnumerator()).ConfigureAwait(false);
diff --git a/src/Cassandra.Tests/SessionTests.cs b/src/Cassandra.Tests/SessionTests.cs
index 4657b03ae..a8058f7d7 100644
--- a/src/Cassandra.Tests/SessionTests.cs
+++ b/src/Cassandra.Tests/SessionTests.cs
@@ -34,7 +34,7 @@ public void Should_GenerateNewSessionId_When_SessionIsCreated()
var sessionNames = new ConcurrentQueue();
var sessionFactoryMock = Mock.Of();
Mock.Get(sessionFactoryMock).Setup(s =>
- s.CreateSessionAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ s.CreateSession(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(Mock.Of())
.Callback((c, ks, serializer, name) => { sessionNames.Enqueue(name); });
diff --git a/src/Cassandra/Builder.cs b/src/Cassandra/Builder.cs
index b931d3ff1..894718cda 100644
--- a/src/Cassandra/Builder.cs
+++ b/src/Cassandra/Builder.cs
@@ -1050,7 +1050,7 @@ public Builder WithExecutionProfiles(Action profileOpt
/// specifies what is the default for each option.
///
///
- /// In case you disable Metadata synchronization, please ensure you invoke in order to keep the token metadata up to date
+ /// In case you disable Metadata synchronization, please ensure you invoke (accessible via ) in order to keep the token metadata up to date
/// otherwise you will not be getting everything you can out of token aware routing, i.e. , which is enabled by the default.
///
///
@@ -1061,7 +1061,7 @@ public Builder WithExecutionProfiles(Action profileOpt
///
/// Token metadata will not be computed and stored.
/// This means that token aware routing (, enabled by default) will only work correctly
- /// if you keep the token metadata up to date using the method.
+ /// if you keep the token metadata up to date using the method (accessible via ).
/// If you wish to go this route of manually refreshing the metadata then
/// it's recommended to refresh only the keyspaces that this application will use, by passing the keyspace parameter.
///
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 6ce58e10b..32f6be9d0 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -39,15 +39,19 @@ namespace Cassandra
///
public class Cluster : IInternalCluster
{
+ private const int Disposed = 10;
+ private const int Initialized = 5;
+ private const int Initializing = 1;
+
private static readonly IPEndPoint DefaultContactPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9042);
private static ProtocolVersion _maxProtocolVersion = ProtocolVersion.MaxSupported;
internal static readonly Logger Logger = new Logger(typeof(Cluster));
private readonly CopyOnWriteList _connectedSessions = new CopyOnWriteList();
private readonly IControlConnection _controlConnection;
- private volatile bool _initialized;
+ private readonly SerializerManager _serializerManager;
private volatile Exception _initException;
- private readonly SemaphoreSlim _initLock = new SemaphoreSlim(1, 1);
+ private readonly SemaphoreSlim _sessionCreateLock = new SemaphoreSlim(1, 1);
private long _sessionCounter = -1;
private readonly bool _implicitContactPoint = false;
@@ -55,13 +59,15 @@ public class Cluster : IInternalCluster
private readonly IProtocolEventDebouncer _protocolEventDebouncer;
private IReadOnlyList _loadBalancingPolicies;
- ///
- public event Action HostAdded;
-
- ///
- public event Action HostRemoved;
+ private readonly Task _initTask;
+ private long _state = Cluster.Initializing;
+
internal IInternalCluster InternalRef => this;
+
+ private bool IsDisposed => Interlocked.Read(ref _state) == Cluster.Disposed;
+
+ ISerializerManager IInternalCluster.SerializerManager => _serializerManager;
///
IControlConnection IInternalCluster.GetControlConnection()
@@ -142,19 +148,23 @@ public static int MaxProtocolVersion
bool IInternalCluster.ImplicitContactPoint => _implicitContactPoint;
///
- public Metadata Metadata
+ public Metadata Metadata => TaskHelper.WaitToComplete(GetMetadataAsync());
+
+ ///
+ public Task GetMetadataAsync()
{
- get
- {
- TaskHelper.WaitToComplete(Init());
- return _metadata;
- }
+ return InternalRef.TryInitAndGetMetadataAsync();
}
private Cluster(IEnumerable
///
+ /// The information about the session instance for which the policy is created.
/// Keyspace on which the query is going to be executed
/// the query for which to build the plan.
/// a new query plan, i.e. an iterator indicating which host to try
/// first for querying, which one to use as failover, etc...
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query)
{
var startIndex = Interlocked.Increment(ref _index);
@@ -126,7 +123,7 @@ public IEnumerable NewQueryPlan(string keyspace, IStatement query)
Interlocked.Exchange(ref _index, 0);
}
- var hosts = GetHosts();
+ var hosts = GetHosts(metadata);
//Round-robin through local nodes
for (var i = 0; i < hosts.Count; i++)
{
@@ -148,7 +145,7 @@ private string GetDatacenter(Host host)
///
/// Gets a tuple containing the list of local and remote nodes
///
- internal List GetHosts()
+ internal List GetHosts(Metadata metadata)
{
var hosts = _hosts;
if (hosts != null)
@@ -166,7 +163,7 @@ internal List GetHosts()
}
//shallow copy the nodes
- var allNodes = _cluster.AllHosts().ToArray();
+ var allNodes = metadata.AllHosts().ToArray();
hosts = allNodes.Where(h => GetDatacenter(h) == LocalDc).ToList();
_hosts = hosts;
@@ -174,4 +171,4 @@ internal List GetHosts()
return hosts;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
index 48394e9a3..eaab841b4 100644
--- a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
@@ -84,29 +84,29 @@ public HostDistance Distance(Host host)
///
/// Initializes the policy.
///
- public void Initialize(ICluster cluster)
+ public void Initialize(Metadata metadata)
{
- ChildPolicy.Initialize(cluster);
+ ChildPolicy.Initialize(metadata);
}
///
/// Returns the hosts to used for a query.
///
- public IEnumerable NewQueryPlan(string keyspace, IStatement statement)
+ public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement statement)
{
if (statement is TargettedSimpleStatement targetedStatement && targetedStatement.PreferredHost != null)
{
_lastPreferredHost = targetedStatement.PreferredHost;
- return YieldPreferred(keyspace, targetedStatement);
+ return YieldPreferred(metadata, keyspace, targetedStatement);
}
- return ChildPolicy.NewQueryPlan(keyspace, statement);
+ return ChildPolicy.NewQueryPlan(metadata, keyspace, statement);
}
- private IEnumerable YieldPreferred(string keyspace, TargettedSimpleStatement statement)
+ private IEnumerable YieldPreferred(Metadata metadata, string keyspace, TargettedSimpleStatement statement)
{
yield return statement.PreferredHost;
- foreach (var h in ChildPolicy.NewQueryPlan(keyspace, statement))
+ foreach (var h in ChildPolicy.NewQueryPlan(metadata, keyspace, statement))
{
yield return h;
}
diff --git a/src/Cassandra/Policies/ILoadBalancingPolicy.cs b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
index 00a48f12f..5638ab6f4 100644
--- a/src/Cassandra/Policies/ILoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
@@ -32,8 +32,8 @@ public interface ILoadBalancingPolicy
/// before any call to another of the methods of the policy.
///
///
- /// The information about the session instance for which the policy is created.
- void Initialize(ICluster cluster);
+ /// The information about the session instance for which the policy is created.
+ void Initialize(Metadata metadata);
///
/// Returns the distance assigned by this policy to the provided host.
The
@@ -58,11 +58,12 @@ public interface ILoadBalancingPolicy
/// be so), the next host will be used. If all hosts of the returned
/// Iterator are down, the query will fail.
///
+ /// The information about the session instance for which the policy is created.
/// The query for which to build a plan, it can be null.
/// Keyspace on which the query is going to be executed, it can be null.
/// An iterator of Host. The query is tried against the hosts returned
/// by this iterator in order, until the query has been sent successfully to one
/// of the host.
- IEnumerable NewQueryPlan(string keyspace, IStatement query);
+ IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query);
}
}
diff --git a/src/Cassandra/Policies/ILocalDatacenterProvider.cs b/src/Cassandra/Policies/ILocalDatacenterProvider.cs
index 481b383c0..1a9bba535 100644
--- a/src/Cassandra/Policies/ILocalDatacenterProvider.cs
+++ b/src/Cassandra/Policies/ILocalDatacenterProvider.cs
@@ -30,6 +30,6 @@ internal interface ILocalDatacenterProvider
///
/// Should be called after we have an initialized cluster instance.
///
- void Initialize(IInternalCluster cluster);
+ void Initialize(IInternalCluster cluster, Metadata metadata);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
index f470bb4ad..adcc7d0ef 100644
--- a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
@@ -28,7 +28,7 @@ public interface ISpeculativeExecutionPolicy : IDisposable
///
/// Initializes the policy at cluster startup.
///
- void Initialize(ICluster cluster);
+ void Initialize(Metadata metadata);
///
/// Returns the plan to use for a new query.
diff --git a/src/Cassandra/Policies/LocalDatacenterProvider.cs b/src/Cassandra/Policies/LocalDatacenterProvider.cs
index 07eedeca6..fcad9f454 100644
--- a/src/Cassandra/Policies/LocalDatacenterProvider.cs
+++ b/src/Cassandra/Policies/LocalDatacenterProvider.cs
@@ -1,12 +1,12 @@
-//
+//
// Copyright (C) DataStax Inc.
-//
+//
// 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.
@@ -16,23 +16,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
+
using Cassandra.SessionManagement;
namespace Cassandra
{
internal class LocalDatacenterProvider : ILocalDatacenterProvider
{
- private bool _initialized = false;
+ private volatile bool _initialized = false;
- private IInternalCluster _cluster;
- private IEnumerable _availableDcs;
- private string _availableDcsStr;
- private string _cachedDatacenter;
+ private volatile IInternalCluster _cluster;
+ private volatile IEnumerable _availableDcs;
+ private volatile string _availableDcsStr;
+ private volatile string _cachedDatacenter;
- public void Initialize(IInternalCluster cluster)
+ public void Initialize(IInternalCluster cluster, Metadata metadata)
{
_cluster = cluster;
- _availableDcs = _cluster.AllHosts().Select(h => h.Datacenter).Where(dc => dc != null).Distinct().ToList();
+ _availableDcs = metadata.AllHosts().Select(h => h.Datacenter).Where(dc => dc != null).Distinct().ToList();
_availableDcsStr = string.Join(", ", _availableDcs);
_initialized = true;
}
@@ -67,7 +68,7 @@ public string DiscoverLocalDatacenter(bool inferLocalDc, string policyDatacenter
"It can be specified in the load balancing policy constructor or " +
$"via the Builder.WithLocalDatacenter() method. Available datacenters: {_availableDcsStr}.");
}
-
+
// implicit contact point so infer the local datacenter from the control connection host
return InferLocalDatacenter();
}
@@ -93,13 +94,13 @@ private string InferLocalDatacenter()
}
// Use the host used by the control connection
- _cachedDatacenter =
- cc.Host?.Datacenter ??
+ _cachedDatacenter =
+ cc.Host?.Datacenter ??
throw new InvalidOperationException(
"The local datacenter could not be inferred from the implicit contact point, " +
"please set it explicitly in the load balancing policy constructor or " +
$"via the Builder.WithLocalDatacenter() method. Available datacenters: {_availableDcsStr}.");
-
+
return _cachedDatacenter;
}
}
diff --git a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
index 500aa96e7..2c956e6c3 100644
--- a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
@@ -36,7 +36,7 @@ public void Dispose()
}
- public void Initialize(ICluster cluster)
+ public void Initialize(Metadata metadata)
{
}
diff --git a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
index 1cbd7c079..2346f623a 100644
--- a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
@@ -34,9 +34,9 @@ public RetryLoadBalancingPolicy(ILoadBalancingPolicy loadBalancingPolicy, IRecon
public ILoadBalancingPolicy LoadBalancingPolicy { get; }
- public void Initialize(ICluster cluster)
+ public void Initialize(Metadata metadata)
{
- LoadBalancingPolicy.Initialize(cluster);
+ LoadBalancingPolicy.Initialize(metadata);
}
public HostDistance Distance(Host host)
@@ -44,12 +44,12 @@ public HostDistance Distance(Host host)
return LoadBalancingPolicy.Distance(host);
}
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query)
{
IReconnectionSchedule schedule = ReconnectionPolicy.NewSchedule();
while (true)
{
- IEnumerable childQueryPlan = LoadBalancingPolicy.NewQueryPlan(keyspace, query);
+ IEnumerable childQueryPlan = LoadBalancingPolicy.NewQueryPlan(metadata, keyspace, query);
foreach (Host host in childQueryPlan)
yield return host;
diff --git a/src/Cassandra/Policies/RoundRobinPolicy.cs b/src/Cassandra/Policies/RoundRobinPolicy.cs
index 990839ead..16946f990 100644
--- a/src/Cassandra/Policies/RoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/RoundRobinPolicy.cs
@@ -14,15 +14,14 @@
// limitations under the License.
//
-using System.Collections.Generic;
-using System.Threading;
+using System.Collections.Generic;
using System.Linq;
-
+using System.Threading;
namespace Cassandra
{
///
- /// A Round-robin load balancing policy.
+ /// A Round-robin load balancing policy.
/// This policy queries nodes in a
/// round-robin fashion. For a given query, if an host fail, the next one
/// (following the round-robin order) is tried, until all hosts have been tried.
@@ -35,12 +34,10 @@ namespace Cassandra
///
public class RoundRobinPolicy : ILoadBalancingPolicy
{
- ICluster _cluster;
- int _index;
+ private int _index;
- public void Initialize(ICluster cluster)
+ public void Initialize(Metadata metadata)
{
- this._cluster = cluster;
}
///
@@ -62,14 +59,15 @@ public HostDistance Distance(Host host)
/// plans returned will cycle over all the host of the cluster in a round-robin
/// fashion.
///
+ /// The information about the session instance for which the policy is created.
/// Keyspace on which the query is going to be executed
/// the query for which to build the plan.
/// a new query plan, i.e. an iterator indicating which host to try
/// first for querying, which one to use as failover, etc...
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query)
{
//shallow copy the all hosts
- var hosts = (from h in _cluster.AllHosts() select h).ToArray();
+ var hosts = (from h in metadata.AllHosts() select h).ToArray();
var startIndex = Interlocked.Increment(ref _index);
//Simplified overflow protection
@@ -84,4 +82,4 @@ public IEnumerable NewQueryPlan(string keyspace, IStatement query)
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Cassandra/Policies/TokenAwarePolicy.cs b/src/Cassandra/Policies/TokenAwarePolicy.cs
index ddd1ccfe5..e4ed87175 100644
--- a/src/Cassandra/Policies/TokenAwarePolicy.cs
+++ b/src/Cassandra/Policies/TokenAwarePolicy.cs
@@ -27,14 +27,13 @@ namespace Cassandra
///
///
/// The method is inherited from the child policy.
- /// The host yielded by the method will first return the
+ /// The host yielded by the method will first return the
/// replicas for the statement, based on the .
///
///
///
public class TokenAwarePolicy : ILoadBalancingPolicy
{
- private ICluster _cluster;
private readonly ThreadLocal _prng = new ThreadLocal(() => new Random(
// Predictable random numbers are OK
Environment.TickCount * Environment.CurrentManagedThreadId));
@@ -52,10 +51,9 @@ public TokenAwarePolicy(ILoadBalancingPolicy childPolicy)
public ILoadBalancingPolicy ChildPolicy { get; }
- public void Initialize(ICluster cluster)
+ public void Initialize(Metadata metadata)
{
- _cluster = cluster;
- ChildPolicy.Initialize(cluster);
+ ChildPolicy.Initialize(metadata);
}
///
@@ -77,16 +75,17 @@ public HostDistance Distance(Host host)
/// IStatement.RoutingKey is not null). Following what
/// it will return the plan of the child policy.
///
+ /// The information about the session instance for which the policy is created.
/// Keyspace on which the query is going to be executed
/// the query for which to build the plan.
/// the new query plan.
- public IEnumerable NewQueryPlan(string loggedKeyspace, IStatement query)
+ public IEnumerable NewQueryPlan(Metadata metadata, string loggedKeyspace, IStatement query)
{
var routingKey = query?.RoutingKey;
IEnumerable childIterator;
if (routingKey == null)
{
- childIterator = ChildPolicy.NewQueryPlan(loggedKeyspace, query);
+ childIterator = ChildPolicy.NewQueryPlan(metadata, loggedKeyspace, query);
foreach (var h in childIterator)
{
yield return h;
@@ -95,7 +94,7 @@ public IEnumerable NewQueryPlan(string loggedKeyspace, IStatement query)
}
var keyspace = query.Keyspace ?? loggedKeyspace;
- var replicas = _cluster.GetReplicas(keyspace, routingKey.RawRoutingKey);
+ var replicas = metadata.GetReplicas(keyspace, routingKey.RawRoutingKey);
var localReplicaSet = new HashSet();
var localReplicaList = new List(replicas.Count);
@@ -117,7 +116,7 @@ public IEnumerable NewQueryPlan(string loggedKeyspace, IStatement query)
}
// Then, return the rest of child policy hosts
- childIterator = ChildPolicy.NewQueryPlan(loggedKeyspace, query);
+ childIterator = ChildPolicy.NewQueryPlan(metadata, loggedKeyspace, query);
foreach (var h in childIterator)
{
if (localReplicaSet.Contains(h))
diff --git a/src/Cassandra/ProtocolEvents/ProtocolEventDebouncer.cs b/src/Cassandra/ProtocolEvents/ProtocolEventDebouncer.cs
index 92bcc7ec2..c46037427 100644
--- a/src/Cassandra/ProtocolEvents/ProtocolEventDebouncer.cs
+++ b/src/Cassandra/ProtocolEvents/ProtocolEventDebouncer.cs
@@ -16,6 +16,7 @@
using System;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
@@ -32,7 +33,8 @@ internal class ProtocolEventDebouncer : IProtocolEventDebouncer
private readonly ActionBlock, ProtocolEvent, bool>> _enqueueBlock;
private readonly ActionBlock _processQueueBlock;
private readonly SlidingWindowExclusiveTimer _timer;
-
+
+ private int _disposed = 0;
private volatile EventQueue _queue = null;
public ProtocolEventDebouncer(ITimerFactory timerFactory, TimeSpan delay, TimeSpan maxDelay)
@@ -114,6 +116,12 @@ public async Task HandleEventAsync(ProtocolEvent ev, bool processNow)
///
public async Task ShutdownAsync()
{
+ // Dispose once
+ if (Interlocked.Increment(ref _disposed) != 1)
+ {
+ return;
+ }
+
_enqueueBlock.Complete();
await _enqueueBlock.Completion.ConfigureAwait(false);
diff --git a/src/Cassandra/ProtocolVersion.cs b/src/Cassandra/ProtocolVersion.cs
index a0054210b..4b2e18e29 100644
--- a/src/Cassandra/ProtocolVersion.cs
+++ b/src/Cassandra/ProtocolVersion.cs
@@ -313,6 +313,14 @@ public static bool CanStartupResponseErrorBeWrapped(this ProtocolVersion version
return version >= ProtocolVersion.V4;
}
+ ///
+ /// Determines whether UDTs are supported in the provided protocol version.
+ ///
+ public static bool SupportsUserDefinedTypes(this ProtocolVersion version)
+ {
+ return version >= ProtocolVersion.V3;
+ }
+
public static int GetHeaderSize(this ProtocolVersion version)
{
if (version.Uses2BytesStreamIds())
diff --git a/src/Cassandra/QueryTrace.cs b/src/Cassandra/QueryTrace.cs
index 1c082f8de..c3df980f0 100644
--- a/src/Cassandra/QueryTrace.cs
+++ b/src/Cassandra/QueryTrace.cs
@@ -33,8 +33,8 @@ namespace Cassandra
public class QueryTrace
{
private readonly object _fetchLock = new object();
- private readonly Metadata _metadata;
private readonly Guid _traceId;
+ private readonly ISession _session;
private IPAddress _coordinator;
private int _duration = int.MinValue;
private List _events;
@@ -177,7 +177,7 @@ public QueryTrace(Guid traceId, ISession session)
//The instance is created before fetching the actual trace metadata
//The properties will be populated later.
_traceId = traceId;
- _metadata = session.Cluster.Metadata;
+ _session = session;
_metadataFetchSyncTimeout = session.Cluster.Configuration.DefaultRequestOptions.QueryAbortTimeout;
}
@@ -227,12 +227,13 @@ private void DoFetchTrace()
}
}
- internal Task LoadAsync()
+ internal async Task LoadAsync()
{
// mark as disconnected, guaranteeing that it wont make metadata fetches triggered by a property get
// ReSharper disable once InconsistentlySynchronizedField : Can be both async and sync, don't mind
_isDisconnected = false;
- return _metadata.GetQueryTraceAsync(this);
+ var metadata = await _session.Cluster.GetMetadataAsync().ConfigureAwait(false);
+ return await metadata.GetQueryTraceAsync(this).ConfigureAwait(false);
}
///
diff --git a/src/Cassandra/Requests/IPrepareHandler.cs b/src/Cassandra/Requests/IPrepareHandler.cs
index 9b0f63657..2e0c92a21 100644
--- a/src/Cassandra/Requests/IPrepareHandler.cs
+++ b/src/Cassandra/Requests/IPrepareHandler.cs
@@ -23,7 +23,7 @@ namespace Cassandra.Requests
{
internal interface IPrepareHandler
{
- Task Prepare(
- PrepareRequest request, IInternalSession session, IEnumerator queryPlan);
+ Task PrepareAsync(
+ PrepareRequest request, IInternalSession session, Metadata metadata, IEnumerator queryPlan);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Requests/IRequestHandler.cs b/src/Cassandra/Requests/IRequestHandler.cs
index da12db4b2..3ad0f0d92 100644
--- a/src/Cassandra/Requests/IRequestHandler.cs
+++ b/src/Cassandra/Requests/IRequestHandler.cs
@@ -29,6 +29,8 @@ namespace Cassandra.Requests
///
internal interface IRequestHandler
{
+ Metadata Metadata { get; }
+
IRequestOptions RequestOptions { get; }
IExtendedRetryPolicy RetryPolicy { get; }
@@ -46,6 +48,11 @@ internal interface IRequestHandler
/// Marks this instance as completed (if not already) and in a new Task using the default scheduler, it invokes the action and sets the result
///
bool SetCompleted(RowSet result, Action action);
+
+ ///
+ /// Marks this instance as completed (if not already) and in a new Task using the default scheduler, it awaits the task and sets the result
+ ///
+ bool SetCompletedWithTask(RowSet result, Func task);
void SetNoMoreHosts(NoHostAvailableException ex, IRequestExecution execution);
diff --git a/src/Cassandra/Requests/IRequestHandlerFactory.cs b/src/Cassandra/Requests/IRequestHandlerFactory.cs
index 1235c8dd7..2a48e85ef 100644
--- a/src/Cassandra/Requests/IRequestHandlerFactory.cs
+++ b/src/Cassandra/Requests/IRequestHandlerFactory.cs
@@ -22,10 +22,17 @@ namespace Cassandra.Requests
{
internal interface IRequestHandlerFactory
{
- IRequestHandler Create(IInternalSession session, ISerializer serializer, IRequest request, IStatement statement, IRequestOptions options);
+ IRequestHandler Create(
+ IInternalSession session,
+ Metadata metadata,
+ ISerializer serializer,
+ IRequest request,
+ IStatement statement,
+ IRequestOptions options);
- IRequestHandler Create(IInternalSession session, ISerializer serializer, IStatement statement, IRequestOptions options);
+ IRequestHandler Create(
+ IInternalSession session, Metadata metadata, ISerializer serializer, IStatement statement, IRequestOptions options);
- IRequestHandler Create(IInternalSession session, ISerializer serializer);
+ IRequestHandler Create(IInternalSession session, Metadata metadata, ISerializer serializer);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Requests/PrepareHandler.cs b/src/Cassandra/Requests/PrepareHandler.cs
index c8d22bb0a..dcc726758 100644
--- a/src/Cassandra/Requests/PrepareHandler.cs
+++ b/src/Cassandra/Requests/PrepareHandler.cs
@@ -42,10 +42,10 @@ public PrepareHandler(ISerializerManager serializerManager, IInternalCluster clu
_reprepareHandler = reprepareHandler;
}
- public async Task Prepare(
- PrepareRequest request, IInternalSession session, IEnumerator queryPlan)
+ public async Task PrepareAsync(
+ PrepareRequest request, IInternalSession session, Metadata metadata, IEnumerator queryPlan)
{
- var prepareResult = await SendRequestToOneNode(session, queryPlan, request).ConfigureAwait(false);
+ var prepareResult = await SendRequestToOneNode(session, metadata, queryPlan, request).ConfigureAwait(false);
if (session.Cluster.Configuration.QueryOptions.IsPrepareOnAllHosts())
{
@@ -55,7 +55,8 @@ public async Task Prepare(
return prepareResult.PreparedStatement;
}
- private async Task SendRequestToOneNode(IInternalSession session, IEnumerator queryPlan, PrepareRequest request)
+ private async Task SendRequestToOneNode(
+ IInternalSession session, Metadata metadata, IEnumerator queryPlan, PrepareRequest request)
{
var triedHosts = new Dictionary();
@@ -70,7 +71,9 @@ private async Task SendRequestToOneNode(IInternalSession session,
var result = await connection.Send(request).ConfigureAwait(false);
return new PrepareResult
{
- PreparedStatement = await GetPreparedStatement(result, request, request.Keyspace ?? connection.Keyspace, session.Cluster).ConfigureAwait(false),
+ PreparedStatement =
+ await GetPreparedStatement(
+ result, request, request.Keyspace ?? connection.Keyspace, metadata).ConfigureAwait(false),
TriedHosts = triedHosts,
HostAddress = host.Address
};
@@ -127,7 +130,7 @@ private Host GetNextHost(IEnumerator queryPlan, out HostDistance distance)
}
private async Task GetPreparedStatement(
- Response response, PrepareRequest request, string keyspace, ICluster cluster)
+ Response response, PrepareRequest request, string keyspace, Metadata metadata)
{
if (response == null)
{
@@ -154,11 +157,11 @@ private async Task GetPreparedStatement(
{
IncomingPayload = resultResponse.CustomPayload
};
- await FillRoutingInfo(ps, cluster).ConfigureAwait(false);
+ await FillRoutingInfo(ps, metadata).ConfigureAwait(false);
return ps;
}
- private static async Task FillRoutingInfo(PreparedStatement ps, ICluster cluster)
+ private static async Task FillRoutingInfo(PreparedStatement ps, Metadata metadata)
{
var column = ps.Variables.Columns.FirstOrDefault();
if (column?.Keyspace == null)
@@ -181,7 +184,7 @@ private static async Task FillRoutingInfo(PreparedStatement ps, ICluster cluster
try
{
const string msgRoutingNotSet = "Routing information could not be set for query \"{0}\"";
- var table = await cluster.Metadata.GetTableAsync(column.Keyspace, column.Table).ConfigureAwait(false);
+ var table = await metadata.GetTableAsync(column.Keyspace, column.Table).ConfigureAwait(false);
if (table == null)
{
Logger.Info(msgRoutingNotSet, ps.Cql);
diff --git a/src/Cassandra/Requests/ReprepareHandler.cs b/src/Cassandra/Requests/ReprepareHandler.cs
index 685ccc0ac..b7d0618b1 100644
--- a/src/Cassandra/Requests/ReprepareHandler.cs
+++ b/src/Cassandra/Requests/ReprepareHandler.cs
@@ -31,7 +31,8 @@ public async Task ReprepareOnAllNodesWithExistingConnections(
IInternalSession session, PrepareRequest request, PrepareResult prepareResult)
{
var pools = session.GetPools();
- var hosts = session.InternalCluster.AllHosts();
+ var metadata = await session.Cluster.GetMetadataAsync().ConfigureAwait(false);
+ var hosts = metadata.AllHosts();
var poolsByHosts = pools.Join(
hosts, po => po.Key,
h => h.Address,
diff --git a/src/Cassandra/Requests/RequestExecution.cs b/src/Cassandra/Requests/RequestExecution.cs
index bde33115d..229ef4bf1 100644
--- a/src/Cassandra/Requests/RequestExecution.cs
+++ b/src/Cassandra/Requests/RequestExecution.cs
@@ -47,7 +47,11 @@ internal class RequestExecution : IRequestExecution
///
private volatile Host _host;
- public RequestExecution(IRequestHandler parent, IInternalSession session, IRequest request, IRequestObserver requestObserver)
+ public RequestExecution(
+ IRequestHandler parent,
+ IInternalSession session,
+ IRequest request,
+ IRequestObserver requestObserver)
{
_parent = parent;
_session = session;
@@ -210,7 +214,7 @@ private void HandleRowSetResult(Response response)
if (resultResponse.Output is OutputSchemaChange schemaChange)
{
//Schema changes need to do blocking operations
- HandleSchemaChange(resultResponse, schemaChange);
+ HandleSchemaChangeAsync(resultResponse, schemaChange);
return;
}
RowSet rs;
@@ -229,7 +233,7 @@ private void HandleRowSetResult(Response response)
_parent.SetCompleted(null, FillRowSet(rs, resultResponse));
}
- private void HandleSchemaChange(ResultResponse response, OutputSchemaChange schemaChange)
+ private void HandleSchemaChangeAsync(ResultResponse response, OutputSchemaChange schemaChange)
{
var result = FillRowSet(new RowSet(), response);
@@ -237,17 +241,19 @@ private void HandleSchemaChange(ResultResponse response, OutputSchemaChange sche
result.Info.SetSchemaInAgreement(false);
// Wait for the schema change before returning the result
- _parent.SetCompleted(
+ _parent.SetCompletedWithTask(
result,
- () =>
+ async () =>
{
- var schemaAgreed = _session.Cluster.Metadata.WaitForSchemaAgreement(_connection);
+ var schemaAgreed = await _parent.Metadata.WaitForSchemaAgreementAsync(_connection).ConfigureAwait(false);
result.Info.SetSchemaInAgreement(schemaAgreed);
try
{
- TaskHelper.WaitToComplete(
- _session.InternalCluster.GetControlConnection().HandleSchemaChangeEvent(schemaChange.SchemaChangeEventArgs, true),
- _session.Cluster.Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds * 1000);
+ await _session.InternalCluster.GetControlConnection()
+ .HandleSchemaChangeEvent(schemaChange.SchemaChangeEventArgs, true)
+ .WaitToCompleteAsync(
+ _session.Cluster.Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds * 1000)
+ .ConfigureAwait(false);
}
catch (TimeoutException)
{
@@ -322,7 +328,8 @@ private void SetAutoPage(RowSet rs, IInternalSession session)
var request = (IQueryRequest)_parent.BuildRequest();
request.PagingState = pagingState;
- return _session.Cluster.Configuration.RequestHandlerFactory.Create(session, _parent.Serializer, request, statement, _parent.RequestOptions).SendAsync();
+ return _session.Cluster.Configuration.RequestHandlerFactory.Create(
+ session, _parent.Metadata, _parent.Serializer, request, statement, _parent.RequestOptions).SendAsync();
}, _parent.RequestOptions.QueryAbortTimeout, _session.MetricsManager);
}
}
diff --git a/src/Cassandra/Requests/RequestHandler.cs b/src/Cassandra/Requests/RequestHandler.cs
index cca23b0b7..36b469a9b 100644
--- a/src/Cassandra/Requests/RequestHandler.cs
+++ b/src/Cassandra/Requests/RequestHandler.cs
@@ -49,25 +49,37 @@ internal class RequestHandler : IRequestHandler
private ISpeculativeExecutionPlan _executionPlan;
private volatile HashedWheelTimer.ITimeout _nextExecutionTimeout;
private readonly IRequestObserver _requestObserver;
+
public IExtendedRetryPolicy RetryPolicy { get; }
+
public ISerializer Serializer { get; }
+
public IStatement Statement { get; }
+
public IRequestOptions RequestOptions { get; }
+ public Metadata Metadata { get; }
+
///
/// Creates a new instance using a request, the statement and the execution profile.
///
public RequestHandler(
- IInternalSession session, ISerializer serializer, IRequest request, IStatement statement, IRequestOptions requestOptions)
+ IInternalSession session,
+ Metadata metadata,
+ ISerializer serializer,
+ IRequest request,
+ IStatement statement,
+ IRequestOptions requestOptions)
{
_session = session ?? throw new ArgumentNullException(nameof(session));
+ Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
_requestObserver = session.ObserverFactory.CreateRequestObserver();
_requestResultHandler = new TcsMetricsRequestResultHandler(_requestObserver);
_request = request;
Serializer = serializer ?? throw new ArgumentNullException(nameof(session));
Statement = statement;
RequestOptions = requestOptions ?? throw new ArgumentNullException(nameof(requestOptions));
-
+
RetryPolicy = RequestOptions.RetryPolicy;
if (statement?.RetryPolicy != null)
@@ -75,23 +87,34 @@ public RequestHandler(
RetryPolicy = statement.RetryPolicy.Wrap(RetryPolicy);
}
- _queryPlan = RequestHandler.GetQueryPlan(session, statement, RequestOptions.LoadBalancingPolicy).GetEnumerator();
+ _queryPlan = RequestHandler.GetQueryPlan(session, metadata, statement, RequestOptions.LoadBalancingPolicy).GetEnumerator();
}
///
/// Creates a new instance using the statement to build the request.
/// Statement can not be null.
///
- public RequestHandler(IInternalSession session, ISerializer serializer, IStatement statement, IRequestOptions requestOptions)
- : this(session, serializer, RequestHandler.GetRequest(statement, serializer, requestOptions), statement, requestOptions)
+ public RequestHandler(
+ IInternalSession session,
+ Metadata metadata,
+ ISerializer serializer,
+ IStatement statement,
+ IRequestOptions requestOptions)
+ : this(
+ session,
+ metadata,
+ serializer,
+ RequestHandler.GetRequest(statement, serializer, requestOptions),
+ statement,
+ requestOptions)
{
}
///
/// Creates a new instance with no request, suitable for getting a connection.
///
- public RequestHandler(IInternalSession session, ISerializer serializer)
- : this(session, serializer, null, null, session.Cluster.Configuration.DefaultRequestOptions)
+ public RequestHandler(IInternalSession session, Metadata metadata, ISerializer serializer)
+ : this(session, metadata, serializer, null, null, session.Cluster.Configuration.DefaultRequestOptions)
{
}
@@ -100,13 +123,13 @@ public RequestHandler(IInternalSession session, ISerializer serializer)
/// In the special case when a Host is provided at Statement level, it will return a query plan with a single
/// host.
///
- private static IEnumerable GetQueryPlan(ISession session, IStatement statement, ILoadBalancingPolicy lbp)
+ private static IEnumerable GetQueryPlan(ISession session, Metadata metadata, IStatement statement, ILoadBalancingPolicy lbp)
{
// Single host iteration
var host = (statement as Statement)?.Host;
return host == null
- ? lbp.NewQueryPlan(session.Keyspace, statement)
+ ? lbp.NewQueryPlan(metadata, session.Keyspace, statement)
: Enumerable.Repeat(host, 1);
}
@@ -182,19 +205,22 @@ public bool SetCompleted(RowSet result, Action action)
{
return SetCompleted(null, result, action);
}
+
+ ///
+ public bool SetCompletedWithTask(RowSet result, Func task)
+ {
+ return SetCompletedWithTask(null, result, task);
+ }
- ///
- /// Marks this instance as completed.
- /// If ex is not null, sets the exception.
- /// If action is not null, it invokes it using the default task scheduler.
- ///
- private bool SetCompleted(Exception ex, RowSet result, Action action)
+ private bool SetCompletedState()
+ {
+ var finishedNow = Interlocked.CompareExchange(ref _state, RequestHandler.StateCompleted, RequestHandler.StateInit)
+ == RequestHandler.StateInit;
+ return finishedNow;
+ }
+
+ private void CancelRunningExecutions()
{
- var finishedNow = Interlocked.CompareExchange(ref _state, RequestHandler.StateCompleted, RequestHandler.StateInit) == RequestHandler.StateInit;
- if (!finishedNow)
- {
- return false;
- }
//Cancel the current timer
//When the next execution timer is being scheduled at the *same time*
//the timer is not going to be cancelled, in that case, this instance is going to stay alive a little longer
@@ -203,11 +229,21 @@ private bool SetCompleted(Exception ex, RowSet result, Action action)
{
execution.Cancel();
}
+ }
+
+ private bool SetResultException(Exception ex)
+ {
if (ex != null)
{
_requestResultHandler.TrySetException(ex);
return true;
}
+
+ return false;
+ }
+
+ private void InvokeCallbackAndSetResult(RowSet result, Action action)
+ {
if (action != null)
{
//Create a new Task using the default scheduler, invoke the action and set the result
@@ -223,9 +259,82 @@ private bool SetCompleted(Exception ex, RowSet result, Action action)
_requestResultHandler.TrySetException(actionEx);
}
});
- return true;
+ return;
}
+
_requestResultHandler.TrySetResult(result);
+ return;
+ }
+
+ private void AwaitTaskAndSetResult(RowSet result, Func task)
+ {
+ if (task != null)
+ {
+ //Create a new Task using the default scheduler, invoke the action and set the result
+ Task.Run(async () =>
+ {
+ try
+ {
+ await task().ConfigureAwait(false);
+ _requestResultHandler.TrySetResult(result);
+ }
+ catch (Exception actionEx)
+ {
+ _requestResultHandler.TrySetException(actionEx);
+ }
+ });
+ return;
+ }
+
+ _requestResultHandler.TrySetResult(result);
+ return;
+ }
+
+ ///
+ /// Marks this instance as completed.
+ /// If ex is not null, sets the exception.
+ /// If action is not null, it invokes it using the default task scheduler.
+ ///
+ private bool SetCompleted(Exception ex, RowSet result, Action action)
+ {
+ var finishedNow = SetCompletedState();
+ if (!finishedNow)
+ {
+ return false;
+ }
+
+ CancelRunningExecutions();
+
+ if (SetResultException(ex))
+ {
+ return true;
+ }
+
+ InvokeCallbackAndSetResult(result, action);
+ return true;
+ }
+
+ ///
+ /// Marks this instance as completed.
+ /// If ex is not null, sets the exception.
+ /// If action is not null, it invokes it using the default task scheduler.
+ ///
+ private bool SetCompletedWithTask(Exception ex, RowSet result, Func task)
+ {
+ var finishedNow = SetCompletedState();
+ if (!finishedNow)
+ {
+ return false;
+ }
+
+ CancelRunningExecutions();
+
+ if (SetResultException(ex))
+ {
+ return true;
+ }
+
+ AwaitTaskAndSetResult(result, task);
return true;
}
diff --git a/src/Cassandra/Requests/RequestHandlerFactory.cs b/src/Cassandra/Requests/RequestHandlerFactory.cs
index 6763c0fd4..5a85d52a0 100644
--- a/src/Cassandra/Requests/RequestHandlerFactory.cs
+++ b/src/Cassandra/Requests/RequestHandlerFactory.cs
@@ -22,20 +22,26 @@ namespace Cassandra.Requests
{
internal class RequestHandlerFactory : IRequestHandlerFactory
{
- public IRequestHandler Create(IInternalSession session, ISerializer serializer, IRequest request, IStatement statement, IRequestOptions options)
+ public IRequestHandler Create(
+ IInternalSession session,
+ Metadata metadata,
+ ISerializer serializer,
+ IRequest request,
+ IStatement statement,
+ IRequestOptions options)
{
- return new RequestHandler(session, serializer, request, statement, options);
+ return new RequestHandler(session, metadata, serializer, request, statement, options);
}
public IRequestHandler Create(
- IInternalSession session, ISerializer serializer, IStatement statement, IRequestOptions options)
+ IInternalSession session, Metadata metadata, ISerializer serializer, IStatement statement, IRequestOptions options)
{
- return new RequestHandler(session, serializer, statement, options);
+ return new RequestHandler(session, metadata, serializer, statement, options);
}
- public IRequestHandler Create(IInternalSession session, ISerializer serializer)
+ public IRequestHandler Create(IInternalSession session, Metadata metadata, ISerializer serializer)
{
- return new RequestHandler(session, serializer);
+ return new RequestHandler(session, metadata, serializer);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/SchemaParser.cs b/src/Cassandra/SchemaParser.cs
index f6e45cb8f..d8d59fa4c 100644
--- a/src/Cassandra/SchemaParser.cs
+++ b/src/Cassandra/SchemaParser.cs
@@ -494,7 +494,7 @@ public override Task GetAggregateAsync(string keyspaceName, s
ReturnType = DataTypeParser.ParseFqTypeName(row.GetValue("return_type")),
ArgumentTypes = (row.GetValue("argument_types") ?? emptyArray).Select(s => DataTypeParser.ParseFqTypeName(s)).ToArray(),
};
- var initConditionRaw = Deserialize(Cc, row.GetValue("initcond"), aggregate.StateType.TypeCode, aggregate.StateType.TypeInfo);
+ var initConditionRaw = Deserialize(Parent.SerializerManager, row.GetValue("initcond"), aggregate.StateType.TypeCode, aggregate.StateType.TypeInfo);
if (initConditionRaw != null)
{
aggregate.InitialCondition = initConditionRaw.ToString();
@@ -503,13 +503,13 @@ public override Task GetAggregateAsync(string keyspaceName, s
});
}
- private static object Deserialize(IMetadataQueryProvider cc, byte[] buffer, ColumnTypeCode typeCode, IColumnInfo typeInfo)
+ private static object Deserialize(ISerializerManager serializerManager, byte[] buffer, ColumnTypeCode typeCode, IColumnInfo typeInfo)
{
if (buffer == null)
{
return null;
}
- return cc.Serializer.GetCurrentSerializer().Deserialize(buffer, 0, buffer.Length, typeCode, typeInfo);
+ return serializerManager.GetCurrentSerializer().Deserialize(buffer, 0, buffer.Length, typeCode, typeInfo);
}
}
diff --git a/src/Cassandra/Session.cs b/src/Cassandra/Session.cs
index 423616068..dfa2231d1 100644
--- a/src/Cassandra/Session.cs
+++ b/src/Cassandra/Session.cs
@@ -39,19 +39,22 @@ namespace Cassandra
///
public class Session : IInternalSession
{
- private readonly ISerializerManager _serializerManager;
+ private const int Disposed = 10;
+ private const int Initialized = 5;
+ private const int Initializing = 1;
+
private static readonly Logger Logger = new Logger(typeof(Session));
private readonly IThreadSafeDictionary _connectionPool;
private readonly IInternalCluster _cluster;
- private int _disposed;
private volatile string _keyspace;
private readonly IMetricsManager _metricsManager;
private readonly IObserverFactory _observerFactory;
private readonly IInsightsClient _insightsClient;
- internal IInternalSession InternalRef => this;
+ private readonly Task _initTask;
+ private long _state = Session.Initializing;
- public int BinaryProtocolVersion => (int)_serializerManager.GetCurrentSerializer().ProtocolVersion;
+ internal IInternalSession InternalRef => this;
///
public ICluster Cluster => _cluster;
@@ -70,7 +73,7 @@ public class Session : IInternalSession
///
/// Determines if the session is already disposed
///
- public bool IsDisposed => Volatile.Read(ref _disposed) > 0;
+ public bool IsDisposed => Interlocked.Read(ref _state) == Session.Disposed;
///
/// Gets or sets the keyspace
@@ -104,20 +107,18 @@ internal Session(
IInternalCluster cluster,
Configuration configuration,
string keyspace,
- ISerializerManager serializerManager,
string sessionName)
{
- _serializerManager = serializerManager;
_cluster = cluster;
Configuration = configuration;
Keyspace = keyspace;
SessionName = sessionName;
- UserDefinedTypes = new UdtMappingDefinitions(this, serializerManager);
_connectionPool = new CopyOnWriteDictionary();
- _cluster.HostRemoved += OnHostRemoved;
_metricsManager = new MetricsManager(configuration.MetricsProvider, Configuration.MetricsOptions, Configuration.MetricsEnabled, SessionName);
_observerFactory = configuration.ObserverFactoryBuilder.Build(_metricsManager);
_insightsClient = configuration.InsightsClientFactory.Create(cluster, this);
+ UserDefinedTypes = new UdtMappingDefinitions(this);
+ _initTask = Task.Run(InitInternalAsync);
}
///
@@ -151,7 +152,7 @@ public void ChangeKeyspace(string keyspace)
///
public void CreateKeyspace(string keyspace, Dictionary replication = null, bool durableWrites = true)
{
- WaitForSchemaAgreement(Execute(CqlQueryTools.GetCreateKeyspaceCql(keyspace, replication, durableWrites, false)));
+ Execute(CqlQueryTools.GetCreateKeyspaceCql(keyspace, replication, durableWrites, false));
Session.Logger.Info("Keyspace [" + keyspace + "] has been successfully CREATED.");
}
@@ -192,24 +193,31 @@ public void Dispose()
{
ShutdownAsync().GetAwaiter().GetResult();
}
-
+
///
public async Task ShutdownAsync()
{
//Only dispose once
- if (Interlocked.Increment(ref _disposed) != 1)
+ var previousState = Interlocked.Exchange(ref _state, Session.Disposed);
+ if (previousState == Session.Disposed)
{
return;
}
+ await ShutdownInternalAsync().ConfigureAwait(false);
+ }
+
+ private async Task ShutdownInternalAsync()
+ {
if (_insightsClient != null)
{
await _insightsClient.ShutdownAsync().ConfigureAwait(false);
}
_metricsManager?.Dispose();
-
- _cluster.HostRemoved -= OnHostRemoved;
+
+ var metadata = await InternalRef.InternalCluster.GetMetadataAsync().ConfigureAwait(false);
+ metadata.HostRemoved -= OnHostRemoved;
var pools = _connectionPool.ToArray();
foreach (var pool in pools)
@@ -218,34 +226,57 @@ public async Task ShutdownAsync()
}
}
- ///
- async Task IInternalSession.Init()
+ Task IInternalSession.TryInitAndGetMetadataAsync()
{
- _metricsManager.InitializeMetrics(this);
+ var currentState = Interlocked.Read(ref _state);
+ if (currentState == Session.Disposed)
+ {
+ throw new ObjectDisposedException("This session object has been disposed.");
+ }
+
+ return _initTask;
+ }
- if (Configuration.GetOrCreatePoolingOptions(_serializerManager.CurrentProtocolVersion).GetWarmup())
+ private async Task InitInternalAsync()
+ {
+ var metadata = await InternalRef.InternalCluster.TryInitAndGetMetadataAsync().ConfigureAwait(false);
+ metadata.HostRemoved += OnHostRemoved;
+
+ _metricsManager.InitializeMetrics(this);
+
+ var serializerManager = _cluster.SerializerManager;
+ if (Configuration.GetOrCreatePoolingOptions(serializerManager.CurrentProtocolVersion).GetWarmup())
{
- await Warmup().ConfigureAwait(false);
+ await WarmupAsync().ConfigureAwait(false);
}
if (Keyspace != null)
{
// Borrow a connection, trying to fail fast
- var handler = Configuration.RequestHandlerFactory.Create(this, _serializerManager.GetCurrentSerializer());
+ var handler = Configuration.RequestHandlerFactory.Create(this, metadata, serializerManager.GetCurrentSerializer());
await handler.GetNextConnectionAsync(new Dictionary()).ConfigureAwait(false);
}
-
- _insightsClient.Init();
+
+ await _insightsClient.InitializeAsync().ConfigureAwait(false);
+
+ var previousState = Interlocked.CompareExchange(ref _state, Session.Initialized, Session.Initializing);
+ if (previousState == Session.Disposed)
+ {
+ await ShutdownInternalAsync().ConfigureAwait(false);
+ }
+
+ return metadata;
}
-
+
///
/// Creates the required connections on all hosts in the local DC.
/// Returns a Task that is marked as completed after all pools were warmed up.
/// In case, all the host pool warmup fail, it logs an error.
///
- private async Task Warmup()
+ private async Task WarmupAsync()
{
- var hosts = _cluster.AllHosts().Where(h => _cluster.RetrieveAndSetDistance(h) == HostDistance.Local).ToArray();
+ var metadata = await _cluster.GetMetadataAsync().ConfigureAwait(false);
+ var hosts = metadata.AllHosts().Where(h => _cluster.RetrieveAndSetDistance(h) == HostDistance.Local).ToArray();
var tasks = new Task[hosts.Length];
for (var i = 0; i < hosts.Length; i++)
{
@@ -337,10 +368,16 @@ public Task ExecuteAsync(IStatement statement, string executionProfileNa
return ExecuteAsync(statement, InternalRef.GetRequestOptions(executionProfileName));
}
- private Task ExecuteAsync(IStatement statement, IRequestOptions requestOptions)
+ private async Task ExecuteAsync(IStatement statement, IRequestOptions requestOptions)
+ {
+ var metadata = await InternalRef.TryInitAndGetMetadataAsync().ConfigureAwait(false);
+ return await ExecuteAsync(metadata, statement, requestOptions).ConfigureAwait(false);
+ }
+
+ private Task ExecuteAsync(Metadata metadata, IStatement statement, IRequestOptions requestOptions)
{
return Configuration.RequestHandlerFactory
- .Create(this, _serializerManager.GetCurrentSerializer(), statement, requestOptions)
+ .Create(this, metadata, metadata.SerializerManager.GetCurrentSerializer(), statement, requestOptions)
.SendAsync();
}
@@ -350,7 +387,7 @@ IHostConnectionPool IInternalSession.GetOrCreateConnectionPool(Host host, HostDi
var hostPool = _connectionPool.GetOrAdd(host.Address, address =>
{
var newPool = Configuration.HostConnectionPoolFactory.Create(
- host, Configuration, _serializerManager, _observerFactory);
+ host, Configuration, _cluster.SerializerManager, _observerFactory);
newPool.AllConnectionClosed += InternalRef.OnAllConnectionClosed;
newPool.SetDistance(distance);
_metricsManager.GetOrCreateNodeMetrics(host).InitializePoolGauges(newPool);
@@ -459,26 +496,7 @@ public Task PrepareAsync(string cqlQuery, string keyspace)
public async Task PrepareAsync(
string cqlQuery, string keyspace, IDictionary customPayload)
{
- var serializer = _serializerManager.GetCurrentSerializer();
- var currentVersion = serializer.ProtocolVersion;
- if (!currentVersion.SupportsKeyspaceInRequest() && keyspace != null)
- {
- // Validate protocol version here and not at PrepareRequest level, as PrepareRequest can be issued
- // in the background (prepare and retry, prepare on up, ...)
- throw new NotSupportedException($"Protocol version {currentVersion} does not support" +
- " setting the keyspace as part of the PREPARE request");
- }
- var request = new PrepareRequest(serializer, cqlQuery, keyspace, customPayload);
- return await _cluster.Prepare(this, _serializerManager, request).ConfigureAwait(false);
- }
-
- public void WaitForSchemaAgreement(RowSet rs)
- {
- }
-
- public bool WaitForSchemaAgreement(IPEndPoint hostAddress)
- {
- return false;
+ return await _cluster.PrepareAsync(this, cqlQuery, keyspace, customPayload).ConfigureAwait(false);
}
private IStatement GetDefaultStatement(string cqlQuery)
@@ -506,7 +524,7 @@ private void OnHostRemoved(Host host)
pool.Dispose();
}
}
-
+
///
public GraphResultSet ExecuteGraph(IGraphStatement statement)
{
@@ -528,15 +546,16 @@ public GraphResultSet ExecuteGraph(IGraphStatement statement, string executionPr
///
public async Task ExecuteGraphAsync(IGraphStatement graphStatement, string executionProfileName)
{
+ var metadata = await InternalRef.TryInitAndGetMetadataAsync().ConfigureAwait(false);
var requestOptions = InternalRef.GetRequestOptions(executionProfileName);
var stmt = graphStatement.ToIStatement(requestOptions.GraphOptions);
- await GetAnalyticsPrimary(stmt, graphStatement, requestOptions).ConfigureAwait(false);
+ await GetAnalyticsPrimary(metadata, stmt, graphStatement, requestOptions).ConfigureAwait(false);
var rs = await ExecuteAsync(stmt, requestOptions).ConfigureAwait(false);
return GraphResultSet.CreateNew(rs, graphStatement, requestOptions.GraphOptions);
}
private async Task GetAnalyticsPrimary(
- IStatement statement, IGraphStatement graphStatement, IRequestOptions requestOptions)
+ Metadata metadata, IStatement statement, IGraphStatement graphStatement, IRequestOptions requestOptions)
{
if (!(statement is TargettedSimpleStatement) || !requestOptions.GraphOptions.IsAnalyticsQuery(graphStatement))
{
@@ -557,10 +576,10 @@ private async Task GetAnalyticsPrimary(
return statement;
}
- return AdaptRpcPrimaryResult(rs, targetedSimpleStatement);
+ return AdaptRpcPrimaryResult(metadata, rs, targetedSimpleStatement);
}
- private IStatement AdaptRpcPrimaryResult(RowSet rowSet, TargettedSimpleStatement statement)
+ private IStatement AdaptRpcPrimaryResult(Metadata metadata, RowSet rowSet, TargettedSimpleStatement statement)
{
var row = rowSet.FirstOrDefault();
if (row == null)
@@ -578,7 +597,7 @@ private IStatement AdaptRpcPrimaryResult(RowSet rowSet, TargettedSimpleStatement
var hostName = location.Substring(0, location.LastIndexOf(':'));
var address = Configuration.AddressTranslator.Translate(
new IPEndPoint(IPAddress.Parse(hostName), Configuration.ProtocolOptions.Port));
- var host = Cluster.GetHost(address);
+ var host = metadata.GetHost(address);
statement.PreferredHost = host;
return statement;
}
diff --git a/src/Cassandra/SessionManagement/IInternalCluster.cs b/src/Cassandra/SessionManagement/IInternalCluster.cs
index d90f02dd5..11a4175b6 100644
--- a/src/Cassandra/SessionManagement/IInternalCluster.cs
+++ b/src/Cassandra/SessionManagement/IInternalCluster.cs
@@ -27,6 +27,8 @@ namespace Cassandra.SessionManagement
///
internal interface IInternalCluster : ICluster
{
+ ISerializerManager SerializerManager { get; }
+
bool AnyOpenConnections(Host host);
///
@@ -51,7 +53,7 @@ internal interface IInternalCluster : ICluster
/// parallel.
/// In case the statement was already in the prepared statements cache, logs an warning but prepares it anyway.
///
- Task Prepare(IInternalSession session, ISerializerManager serializerManager, PrepareRequest request);
+ Task PrepareAsync(IInternalSession session, string cqlQuery, string keyspace, IDictionary customPayload);
IReadOnlyDictionary> GetResolvedEndpoints();
@@ -59,5 +61,10 @@ internal interface IInternalCluster : ICluster
/// Helper method to retrieve the aggregate distance from all configured LoadBalancingPolicies and set it at Host level.
///
HostDistance RetrieveAndSetDistance(Host host);
+
+ ///
+ /// Initializes once (Thread-safe) the control connection and returns the metadata associated with the Cluster instance
+ ///
+ Task TryInitAndGetMetadataAsync();
}
}
\ No newline at end of file
diff --git a/src/Cassandra/SessionManagement/IInternalSession.cs b/src/Cassandra/SessionManagement/IInternalSession.cs
index c8b9e236f..5629a7770 100644
--- a/src/Cassandra/SessionManagement/IInternalSession.cs
+++ b/src/Cassandra/SessionManagement/IInternalSession.cs
@@ -36,10 +36,7 @@ internal interface IInternalSession : ISession
///
Guid InternalSessionId { get; }
- ///
- /// Initialize the session
- ///
- Task Init();
+ Task TryInitAndGetMetadataAsync();
///
/// Gets or creates the connection pool for a given host
diff --git a/src/Cassandra/SessionManagement/ISessionFactory.cs b/src/Cassandra/SessionManagement/ISessionFactory.cs
index 8bcab0372..ef918fd51 100644
--- a/src/Cassandra/SessionManagement/ISessionFactory.cs
+++ b/src/Cassandra/SessionManagement/ISessionFactory.cs
@@ -14,14 +14,11 @@
// limitations under the License.
//
-using System.Threading.Tasks;
-using Cassandra.Serialization;
-
namespace Cassandra.SessionManagement
{
internal interface ISessionFactory
{
- Task CreateSessionAsync(
- IInternalCluster cluster, string keyspace, ISerializerManager serializer, string sessionName);
+ IInternalSession CreateSession(
+ IInternalCluster cluster, string keyspace, string sessionName);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/SessionManagement/SessionFactory.cs b/src/Cassandra/SessionManagement/SessionFactory.cs
index f27526762..4f7878fbe 100644
--- a/src/Cassandra/SessionManagement/SessionFactory.cs
+++ b/src/Cassandra/SessionManagement/SessionFactory.cs
@@ -14,18 +14,14 @@
// limitations under the License.
//
-using System.Threading.Tasks;
-using Cassandra.Serialization;
-
namespace Cassandra.SessionManagement
{
internal class SessionFactory : ISessionFactory
{
- public Task CreateSessionAsync(
- IInternalCluster cluster, string keyspace, ISerializerManager serializer, string sessionName)
+ public IInternalSession CreateSession(
+ IInternalCluster cluster, string keyspace, string sessionName)
{
- return Task.FromResult(
- new Session(cluster, cluster.Configuration, keyspace, serializer, sessionName).InternalRef);
+ return new Session(cluster, cluster.Configuration, keyspace, sessionName).InternalRef;
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/SessionState.cs b/src/Cassandra/SessionState.cs
index d3cb4faa3..858261623 100644
--- a/src/Cassandra/SessionState.cs
+++ b/src/Cassandra/SessionState.cs
@@ -62,13 +62,13 @@ public override string ToString()
return builder.ToString();
}
- internal static SessionState From(IInternalSession session)
+ internal static SessionState From(IInternalSession session, Metadata metadata)
{
var pools = session.GetPools();
var result = new Dictionary();
foreach (var kv in pools)
{
- var host = session.Cluster.GetHost(kv.Key);
+ var host = metadata.GetHost(kv.Key);
if (host == null)
{
continue;
diff --git a/src/Cassandra/UdtMappingDefinitions.cs b/src/Cassandra/UdtMappingDefinitions.cs
index 96f04c9df..2a1c25d6d 100644
--- a/src/Cassandra/UdtMappingDefinitions.cs
+++ b/src/Cassandra/UdtMappingDefinitions.cs
@@ -18,7 +18,7 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
-using Cassandra.Serialization;
+
using Cassandra.SessionManagement;
using Cassandra.Tasks;
@@ -32,14 +32,12 @@ public class UdtMappingDefinitions
private readonly ConcurrentDictionary _udtByNetType;
private readonly IInternalCluster _cluster;
private readonly IInternalSession _session;
- private readonly ISerializerManager _serializer;
- internal UdtMappingDefinitions(IInternalSession session, ISerializerManager serializer)
+ internal UdtMappingDefinitions(IInternalSession session)
{
_udtByNetType = new ConcurrentDictionary();
_cluster = session.InternalCluster;
_session = session;
- _serializer = serializer;
}
///
@@ -61,6 +59,7 @@ public async Task DefineAsync(params UdtMap[] udtMaps)
{
throw new ArgumentNullException("udtMaps");
}
+ var metadata = await _cluster.GetMetadataAsync().ConfigureAwait(false);
var sessionKeyspace = _session.Keyspace;
if (string.IsNullOrEmpty(sessionKeyspace) && udtMaps.Any(map => map.Keyspace == null))
{
@@ -68,17 +67,17 @@ public async Task DefineAsync(params UdtMap[] udtMaps)
"You can specify it while creating the UdtMap, while creating the Session and" +
" while creating the Cluster (default keyspace config setting).");
}
- if (_session.BinaryProtocolVersion < 3)
+ if (!metadata.ProtocolVersion.SupportsUserDefinedTypes())
{
throw new NotSupportedException("User defined type mapping is supported with C* 2.1+ and protocol version 3+");
}
// Add types to both indexes
foreach (var map in udtMaps)
{
- var udtDefition = await GetDefinitionAsync(map.Keyspace ?? sessionKeyspace, map).ConfigureAwait(false);
- map.SetSerializer(_serializer.GetCurrentSerializer());
+ var udtDefition = await GetDefinitionAsync(map.Keyspace ?? sessionKeyspace, map, metadata).ConfigureAwait(false);
+ map.SetSerializer(metadata.SerializerManager.GetCurrentSerializer());
map.Build(udtDefition);
- _serializer.SetUdtMap(udtDefition.Name, map);
+ metadata.SerializerManager.SetUdtMap(udtDefition.Name, map);
_udtByNetType.AddOrUpdate(map.NetType, map, (k, oldValue) => map);
}
}
@@ -87,7 +86,7 @@ public async Task DefineAsync(params UdtMap[] udtMaps)
/// Gets the definition and validates the fields
///
///
- private async Task GetDefinitionAsync(string keyspace, UdtMap map)
+ private async Task GetDefinitionAsync(string keyspace, UdtMap map, Metadata metadata)
{
var caseSensitiveUdtName = map.UdtName;
if (map.IgnoreCase)
@@ -95,7 +94,8 @@ private async Task GetDefinitionAsync(string keyspace, UdtMap map
//identifiers are lower cased in Cassandra
caseSensitiveUdtName = caseSensitiveUdtName.ToLowerInvariant();
}
- var udtDefinition = await _cluster.Metadata.GetUdtDefinitionAsync(keyspace, caseSensitiveUdtName).ConfigureAwait(false);
+
+ var udtDefinition = await metadata.GetUdtDefinitionAsync(keyspace, caseSensitiveUdtName).ConfigureAwait(false);
if (udtDefinition == null)
{
throw new InvalidTypeException($"{caseSensitiveUdtName} UDT not found on keyspace {keyspace}");
@@ -113,4 +113,4 @@ internal UdtMap GetUdtMap(string keyspace, Type netType)
return _udtByNetType.TryGetValue(netType, out UdtMap map) ? map : null;
}
}
-}
+}
\ No newline at end of file
From cc0ebde7b5f7242da5d0e0340f80cb268e0b25c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Wed, 24 Jun 2020 12:24:03 +0100
Subject: [PATCH 02/31] change LBP and SEP Initialize to be async
---
.../Core/ConnectionSimulacronTests.cs | 2 +-
.../Connections/Control/ControlConnectionTests.cs | 4 ++--
src/Cassandra.Tests/TestHelper.cs | 2 +-
src/Cassandra/Cluster.cs | 4 ++--
.../Policies/ConstantSpeculativeExecutionPolicy.cs | 6 ++++--
src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs | 6 +++++-
src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs | 5 +++--
src/Cassandra/Policies/ILoadBalancingPolicy.cs | 8 +++++++-
src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs | 3 ++-
src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs | 6 ++++--
src/Cassandra/Policies/RetryLoadBalancingPolicy.cs | 5 +++--
src/Cassandra/Policies/RoundRobinPolicy.cs | 5 ++++-
src/Cassandra/Policies/TokenAwarePolicy.cs | 5 +++--
13 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs b/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs
index a6ed492eb..a66a67313 100644
--- a/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/ConnectionSimulacronTests.cs
@@ -382,7 +382,7 @@ public TestDisallowListLbp(ILoadBalancingPolicy parent, params IPEndPoint[] disa
public void Initialize(ICluster cluster)
{
- _parent.Initialize(cluster);
+ _parent.InitializeAsync(cluster);
}
public HostDistance Distance(Host host)
diff --git a/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs b/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs
index 4bdbdc847..db3b8f8a6 100644
--- a/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs
+++ b/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs
@@ -213,7 +213,7 @@ public void Should_NotAttemptDownOrIgnoredHosts()
.Setup(c => c.RetrieveAndSetDistance(It.IsAny()))
.Returns(h => config.Policies.LoadBalancingPolicy.Distance(h));
Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(() => metadata.AllHosts());
- config.Policies.LoadBalancingPolicy.Initialize(cluster);
+ config.Policies.LoadBalancingPolicy.InitializeAsync(cluster);
connectionOpenEnabled = false;
@@ -271,7 +271,7 @@ public async Task Should_NotLeakConnections_When_DisposeAndReconnectHappenSimult
Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(() => metadata.AllHosts());
Mock.Get(cluster).Setup(c => c.GetControlConnection()).Returns(cc);
config.LocalDatacenterProvider.Initialize(cluster);
- config.Policies.LoadBalancingPolicy.Initialize(cluster);
+ config.Policies.LoadBalancingPolicy.InitializeAsync(cluster);
createResult.ConnectionFactory.CreatedConnections.Clear();
diff --git a/src/Cassandra.Tests/TestHelper.cs b/src/Cassandra.Tests/TestHelper.cs
index 08db9850e..1425ef61a 100644
--- a/src/Cassandra.Tests/TestHelper.cs
+++ b/src/Cassandra.Tests/TestHelper.cs
@@ -731,7 +731,7 @@ public OrderedLoadBalancingPolicy()
public void Initialize(ICluster cluster)
{
_hosts = cluster.AllHosts();
- _childPolicy.Initialize(cluster);
+ _childPolicy.InitializeAsync(cluster);
}
public HostDistance Distance(Host host)
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 32f6be9d0..4377a4b9c 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -275,12 +275,12 @@ private async Task InitAsync()
// Initialize policies
foreach (var lbp in loadBalancingPolicies)
{
- lbp.Initialize(_metadata);
+ await lbp.InitializeAsync(_metadata).ConfigureAwait(false);
}
foreach (var sep in speculativeExecutionPolicies)
{
- sep.Initialize(_metadata);
+ await sep.InitializeAsync(_metadata).ConfigureAwait(false);
}
InitializeHostDistances();
diff --git a/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
index 7c654dd49..818ea220d 100644
--- a/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
@@ -15,6 +15,8 @@
//
using System;
+using System.Threading.Tasks;
+using Cassandra.Tasks;
// ReSharper disable once CheckNamespace
namespace Cassandra
@@ -54,9 +56,9 @@ public void Dispose()
}
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
-
+ return TaskHelper.Completed;
}
public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
diff --git a/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs b/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
index fe09e9510..7a1818b3e 100644
--- a/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
@@ -18,6 +18,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
+using Cassandra.Tasks;
namespace Cassandra
{
@@ -76,7 +78,7 @@ public DCAwareRoundRobinPolicy(string localDc) : this(inferLocalDc: false)
///
public string LocalDc { get; private set; }
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
//When the pool changes, it should clear the local cache
metadata.HostAdded += _ => ClearHosts();
@@ -84,6 +86,8 @@ public void Initialize(Metadata metadata)
LocalDc = metadata.Configuration.LocalDatacenterProvider.DiscoverLocalDatacenter(
_inferLocalDc, LocalDc);
+
+ return TaskHelper.Completed;
}
///
diff --git a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
index eaab841b4..e8e01155b 100644
--- a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Cassandra
{
@@ -84,9 +85,9 @@ public HostDistance Distance(Host host)
///
/// Initializes the policy.
///
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
- ChildPolicy.Initialize(metadata);
+ return ChildPolicy.InitializeAsync(metadata);
}
///
diff --git a/src/Cassandra/Policies/ILoadBalancingPolicy.cs b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
index 5638ab6f4..00210c81c 100644
--- a/src/Cassandra/Policies/ILoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
@@ -15,6 +15,7 @@
//
using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Cassandra
{
@@ -25,7 +26,12 @@ namespace Cassandra
public interface ILoadBalancingPolicy
{
///
+ ///
/// Initialize this load balancing policy.
+ ///
+ ///
+ /// If the implementation is not async, we recommend returning Task.CompletedTask or Task.FromResult(0);
+ ///
///
/// Note that the driver guarantees
/// that it will call this method exactly once per policy object and will do so
@@ -33,7 +39,7 @@ public interface ILoadBalancingPolicy
///
///
/// The information about the session instance for which the policy is created.
- void Initialize(Metadata metadata);
+ Task InitializeAsync(Metadata metadata);
///
/// Returns the distance assigned by this policy to the provided host.
The
diff --git a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
index adcc7d0ef..96f2341af 100644
--- a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
@@ -15,6 +15,7 @@
//
using System;
+using System.Threading.Tasks;
// ReSharper disable once CheckNamespace
namespace Cassandra
@@ -28,7 +29,7 @@ public interface ISpeculativeExecutionPolicy : IDisposable
///
/// Initializes the policy at cluster startup.
///
- void Initialize(Metadata metadata);
+ Task InitializeAsync(Metadata metadata);
///
/// Returns the plan to use for a new query.
diff --git a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
index 2c956e6c3..a6f5dbaef 100644
--- a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
@@ -14,6 +14,8 @@
// limitations under the License.
//
+using System.Threading.Tasks;
+using Cassandra.Tasks;
// ReSharper disable once CheckNamespace
namespace Cassandra
@@ -36,9 +38,9 @@ public void Dispose()
}
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
-
+ return TaskHelper.Completed;
}
public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
diff --git a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
index 2346f623a..a89bdc77c 100644
--- a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
@@ -17,6 +17,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using System.Threading.Tasks;
namespace Cassandra
{
@@ -34,9 +35,9 @@ public RetryLoadBalancingPolicy(ILoadBalancingPolicy loadBalancingPolicy, IRecon
public ILoadBalancingPolicy LoadBalancingPolicy { get; }
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
- LoadBalancingPolicy.Initialize(metadata);
+ return LoadBalancingPolicy.InitializeAsync(metadata);
}
public HostDistance Distance(Host host)
diff --git a/src/Cassandra/Policies/RoundRobinPolicy.cs b/src/Cassandra/Policies/RoundRobinPolicy.cs
index 16946f990..422814ee9 100644
--- a/src/Cassandra/Policies/RoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/RoundRobinPolicy.cs
@@ -17,6 +17,8 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
+using Cassandra.Tasks;
namespace Cassandra
{
@@ -36,8 +38,9 @@ public class RoundRobinPolicy : ILoadBalancingPolicy
{
private int _index;
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
+ return TaskHelper.Completed;
}
///
diff --git a/src/Cassandra/Policies/TokenAwarePolicy.cs b/src/Cassandra/Policies/TokenAwarePolicy.cs
index e4ed87175..b3bc0bb51 100644
--- a/src/Cassandra/Policies/TokenAwarePolicy.cs
+++ b/src/Cassandra/Policies/TokenAwarePolicy.cs
@@ -18,6 +18,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
namespace Cassandra
{
@@ -51,9 +52,9 @@ public TokenAwarePolicy(ILoadBalancingPolicy childPolicy)
public ILoadBalancingPolicy ChildPolicy { get; }
- public void Initialize(Metadata metadata)
+ public Task InitializeAsync(Metadata metadata)
{
- ChildPolicy.Initialize(metadata);
+ return ChildPolicy.InitializeAsync(metadata);
}
///
From 37c35f9292e44c0912dae448025a742ddbc71781 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Wed, 24 Jun 2020 13:08:11 +0100
Subject: [PATCH 03/31] fix deadlock in session shutdown
---
src/Cassandra/Session.cs | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/Cassandra/Session.cs b/src/Cassandra/Session.cs
index dfa2231d1..14a005b3f 100644
--- a/src/Cassandra/Session.cs
+++ b/src/Cassandra/Session.cs
@@ -46,7 +46,6 @@ public class Session : IInternalSession
private static readonly Logger Logger = new Logger(typeof(Session));
private readonly IThreadSafeDictionary _connectionPool;
private readonly IInternalCluster _cluster;
- private volatile string _keyspace;
private readonly IMetricsManager _metricsManager;
private readonly IObserverFactory _observerFactory;
private readonly IInsightsClient _insightsClient;
@@ -54,6 +53,9 @@ public class Session : IInternalSession
private readonly Task _initTask;
private long _state = Session.Initializing;
+ private volatile Metadata _initializedMetadata;
+ private volatile string _keyspace;
+
internal IInternalSession InternalRef => this;
///
@@ -199,7 +201,7 @@ public async Task ShutdownAsync()
{
//Only dispose once
var previousState = Interlocked.Exchange(ref _state, Session.Disposed);
- if (previousState == Session.Disposed)
+ if (previousState != Session.Initialized)
{
return;
}
@@ -215,8 +217,8 @@ private async Task ShutdownInternalAsync()
}
_metricsManager?.Dispose();
-
- var metadata = await InternalRef.InternalCluster.GetMetadataAsync().ConfigureAwait(false);
+
+ var metadata = _initializedMetadata;
metadata.HostRemoved -= OnHostRemoved;
var pools = _connectionPool.ToArray();
@@ -258,11 +260,13 @@ private async Task InitInternalAsync()
}
await _insightsClient.InitializeAsync().ConfigureAwait(false);
-
+
+ _initializedMetadata = metadata;
var previousState = Interlocked.CompareExchange(ref _state, Session.Initialized, Session.Initializing);
if (previousState == Session.Disposed)
{
await ShutdownInternalAsync().ConfigureAwait(false);
+ throw new ObjectDisposedException("Session instance was disposed before initialization finished.");
}
return metadata;
From 3d9c2568fae08d35127fc184ce298b0ab31b259f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Wed, 24 Jun 2020 13:34:26 +0100
Subject: [PATCH 04/31] add Session.ConnectAsync()
---
src/Cassandra/Cluster.cs | 12 ++++---
src/Cassandra/ISession.cs | 28 ++++++++++++++++
src/Cassandra/Session.cs | 33 ++++++++++++++++---
.../SessionManagement/IInternalCluster.cs | 3 ++
4 files changed, 66 insertions(+), 10 deletions(-)
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 4377a4b9c..a9182e3f7 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -75,6 +75,8 @@ IControlConnection IInternalCluster.GetControlConnection()
return _controlConnection;
}
+ Exception IInternalCluster.InitException => _initException;
+
///
ConcurrentDictionary IInternalCluster.PreparedQueries { get; }
= new ConcurrentDictionary(new ByteArrayComparer());
@@ -199,15 +201,15 @@ private Cluster(IEnumerable contactPoints, Configuration configuration)
Task IInternalCluster.TryInitAndGetMetadataAsync()
{
var currentState = Interlocked.Read(ref _state);
- if (currentState == Cluster.Disposed)
+ if (currentState == Cluster.Initialized)
{
- throw new ObjectDisposedException("This cluster object has been disposed.");
+ //It was already initialized
+ return _initTask;
}
- if (currentState == Cluster.Initialized)
+ if (currentState == Cluster.Disposed)
{
- //It was already initialized
- return Task.FromResult(_metadata);
+ throw new ObjectDisposedException("This cluster object has been disposed.");
}
if (_initException != null)
diff --git a/src/Cassandra/ISession.cs b/src/Cassandra/ISession.cs
index a815aa2be..b53ade1e1 100644
--- a/src/Cassandra/ISession.cs
+++ b/src/Cassandra/ISession.cs
@@ -65,6 +65,34 @@ public interface ISession: IDisposable
///
string SessionName { get; }
+ ///
+ ///
+ /// Waits until the initialization task is finished (this task is started when the Session is created).
+ ///
+ /// It is not necessary to call this method but you can use it if you want the initialization to happen
+ /// in a specific state of your application (e.g. during startup before your application listens for requests).
+ ///
+ ///
+ /// If your application uses the Task Parallel Library (e.g. async/await) please use instead.
+ ///
+ ///
+ /// If the initialization failed.
+ /// If the initialization timed out.
+ /// If further attempts to connect are made after initialization has failed.
+ void Connect();
+
+ ///
+ ///
+ /// Waits until the initialization task is finished (this task is started when the Session is created).
+ /// It is not necessary to call this method but you can use it if you want the initialization to happen
+ /// in a specific state of your application (e.g. during startup before your application listens for requests).
+ ///
+ ///
+ /// If the initialization failed.
+ /// If the initialization timed out.
+ /// If further attempts to connect are made after initialization has failed.
+ Task ConnectAsync();
+
///
/// Begins asynchronous execute operation.
///
diff --git a/src/Cassandra/Session.cs b/src/Cassandra/Session.cs
index 14a005b3f..815b397dd 100644
--- a/src/Cassandra/Session.cs
+++ b/src/Cassandra/Session.cs
@@ -29,8 +29,6 @@
using Cassandra.Metrics;
using Cassandra.Metrics.Internal;
using Cassandra.Observers.Abstractions;
-using Cassandra.Requests;
-using Cassandra.Serialization;
using Cassandra.SessionManagement;
using Cassandra.Tasks;
@@ -100,6 +98,18 @@ string IInternalSession.Keyspace
public string SessionName { get; }
+ ///
+ public void Connect()
+ {
+ TaskHelper.WaitToComplete(ConnectAsync());
+ }
+
+ ///
+ public Task ConnectAsync()
+ {
+ return InternalRef.TryInitAndGetMetadataAsync();
+ }
+
public Policies Policies => Configuration.Policies;
///
@@ -231,10 +241,23 @@ private async Task ShutdownInternalAsync()
Task IInternalSession.TryInitAndGetMetadataAsync()
{
var currentState = Interlocked.Read(ref _state);
+
+ if (currentState == Session.Initialized)
+ {
+ //It was already initialized
+ return _initTask;
+ }
+
if (currentState == Session.Disposed)
{
throw new ObjectDisposedException("This session object has been disposed.");
}
+
+ if (_cluster.InitException != null)
+ {
+ //There was an exception that is not possible to recover from
+ throw _cluster.InitException;
+ }
return _initTask;
}
@@ -243,9 +266,9 @@ private async Task InitInternalAsync()
{
var metadata = await InternalRef.InternalCluster.TryInitAndGetMetadataAsync().ConfigureAwait(false);
metadata.HostRemoved += OnHostRemoved;
-
+
_metricsManager.InitializeMetrics(this);
-
+
var serializerManager = _cluster.SerializerManager;
if (Configuration.GetOrCreatePoolingOptions(serializerManager.CurrentProtocolVersion).GetWarmup())
{
@@ -260,7 +283,7 @@ private async Task InitInternalAsync()
}
await _insightsClient.InitializeAsync().ConfigureAwait(false);
-
+
_initializedMetadata = metadata;
var previousState = Interlocked.CompareExchange(ref _state, Session.Initialized, Session.Initializing);
if (previousState == Session.Disposed)
diff --git a/src/Cassandra/SessionManagement/IInternalCluster.cs b/src/Cassandra/SessionManagement/IInternalCluster.cs
index 11a4175b6..4c4820bfa 100644
--- a/src/Cassandra/SessionManagement/IInternalCluster.cs
+++ b/src/Cassandra/SessionManagement/IInternalCluster.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -47,6 +48,8 @@ internal interface IInternalCluster : ICluster
///
ConcurrentDictionary PreparedQueries { get; }
+ Exception InitException { get; }
+
///
/// Executes the prepare request on the first host selected by the load balancing policy.
/// When is enabled, it prepares on the rest of the hosts in
From 57cc9a369fa0d9b6dab737ebcb548a9640eccb3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Wed, 24 Jun 2020 13:34:53 +0100
Subject: [PATCH 05/31] add upgrade guide section
---
doc/upgrade-guide/upgrade-to-4x/README.md | 81 ++++++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)
diff --git a/doc/upgrade-guide/upgrade-to-4x/README.md b/doc/upgrade-guide/upgrade-to-4x/README.md
index 22cb79268..2e515f4bf 100644
--- a/doc/upgrade-guide/upgrade-to-4x/README.md
+++ b/doc/upgrade-guide/upgrade-to-4x/README.md
@@ -23,4 +23,83 @@ The `usedHostsPerRemoteDc` parameter on the `DCAwareRoundRobinPolicy` gave the i
A good write up on datacenter failover describing some of these considerations can be found [here][dc-failover-post].
-[dc-failover-post]: https://medium.com/@foundev/cassandra-local-quorum-should-stay-local-c174d555cc57
+## Creation of `ISession` instances returns immediately
+
+In C# driver 4.0, when you create an `ISession` instance, the initialization task will be started in the background. When a request is executed, the driver will wait for the initialization task to finish before sending the request.
+
+If you want to explicitely wait for the initialization to be finished, you can use one of these new methods: `ISession.ConnectAsync()` / `ISession.Connect()`.
+
+## Addition of several async methods (e.g. `ICluster.GetMetadataAsync()`)
+
+The `ICluster.Metadata` property has always blocked until the initialization task is finished but until now there weren't many issues with this behavior because the user would create a session right away which would initialize the `ICluster` instance in a blocking manner.
+
+Now that the initialization happens in the background, it's more likely that the user faces a scenario where the `ICluster.Metadata` blocks which is really bad if the application uses TPL (e.g. `async/await`) because it could block a thread of the shared threadpool.
+
+To fix this, we added an `async` way to retrieve the `Metadata` instance: `ICluster.GetMetadataAsync()`.
+
+If you would like to keep using the `ICluster.Metadata` property, please make sure that you explicitly initialize the session before accessing it in case your application uses `async/await` to avoid blocking the thread.
+
+There were some methods that used the `ICluster.Metadata` property so we have added async variants of those methods as well:
+
+| Existing method | New async method | Namespace |
+|------------------------------|-------------------------------|-----------------------|
+| `CreateBatch(this ISession)` | `ISession.CreateBatchAsync()` | `Cassandra.Data.Linq` |
+| `GetState(this ISession)` | `ISession.GetStateAsync()` | `Cassandra` |
+
+## `Metadata` API changes
+
+Several methods of the `ICluster` interface were actually wrappers around methods that are implemented in the `Metadata` class. We decided to move all metadata related elements from `ICluster`/`ISession` to `Metadata` because of two reasons:
+
+- Simplification of the driver's API, it makes more sense to have metadata related methods, properties and events on the `Metadata` class
+- The cluster/session initialization happens in the background, which means that we would have to add a lot of `async` methods to the `ICluster` interface if we kept them (similar to what we did with `GetMetadataAsync()`)
+
+If you are using one of theses elements that were moved to the `Metadata` class, you can use `ICluster.Metadata` or `ICluster.GetMetadataAsync()` to access it (see previous section for a brief explanation about this).
+
+These are the methods, properties and events that were affected:
+
+| Old API | New API |
+|------------------------------------|-----------------------------------|
+| `ICluster.AllHosts()` | `Metadata.AllHosts()` |
+| `ICluster.GetHost()` | `Metadata.GetHost()` |
+| `ICluster.GetReplicas()` | `Metadata.GetReplicas()` |
+| `ICluster.RefreshSchema()` | `Metadata.RefreshSchema()` |
+| `ICluster.RefreshSchemaAsync()` | `Metadata.RefreshSchemaAsync()` |
+| `ICluster.HostAdded` | `Metadata.HostAdded` |
+| `ICluster.HostRemoved` | `Metadata.HostRemoved` |
+| `ISession.BinaryProtocolVersion` | `Metadata.ProtocolVersion` |
+
+Note: `Metadata.ProtocolVersion` returns an `enum` instead of `int`, you can cast this `enum` to `int` if you need it (`ISession.BinaryProtocolVersion` did this internally).
+
+## Removal of `ISession.WaitForSchemaAgreement()`
+
+When a DDL request is executed, the driver will wait for schema agreement before returning control to the user. See `ProtocolOptions.MaxSchemaAgreementWaitSeconds` for more info.
+
+If you want to manually check for schema agreement you can use the `Metadata.CheckSchemaAgreementAsync()` method.
+
+## `Metadata` no longer implements `IDisposable`
+
+The implementation of `IDisposable` was pretty much empty at this point so we decided to remove it.
+
+We also removed `Metadata.ShutDown()` for the same reason.
+
+## `ILoadBalancingPolicy` interface changes
+
+You are only affected by these changes if you implemented a custom load balancing policy in your application instead of using one of those that are provided by the driver.
+
+### `ILoadBalancingPolicy.Initialize()`
+
+The `Initialize()` method is now `InitializeAsync()` and returns a `Task`. If the implementation is not async, we recommend returning `Task.CompletedTask` or `Task.FromResult(0)`.
+
+ `InitializeAsync()` now has a `Metadata` parameter instead of `ICluster`. You can obtain the hosts collection and replicas using the `Metadata` class (see the earlier section related to `Metadata` API changes for more information on these changes).
+
+### `ILoadBalancingPolicy.NewQueryPlan()`
+
+The `NewQueryPlan()` method now has a `Metadata` parameter in addition to `string` (keyspace) and `IStatement`.
+
+This is to simplify the process of implementing a custom load balancing policy. Previously, all implementations had to be stateful and threadsafe, i.e., the `cluster` object that was provided in the `Initialize()` was necessary in order to implement the `NewQueryPlan()` method.
+
+Now you can build a completely stateless load balancing policy (which is guaranteed to be threadsafe) by obtaining the hosts / replicas via the `Metadata` parameter in the `NewQueryPlan()` method. In this scenario you can have an implementation of the `InitializeAsync()` method that just returns `Task.CompletedTask` or `Task.FromResult(0)`.
+
+You can still build more complex load balancing policies that access some kind of metadata service for example by implementing the `InitializeAsync()` method.
+
+[dc-failover-post]: https://medium.com/@foundev/cassandra-local-quorum-should-stay-local-c174d555cc57
\ No newline at end of file
From 81d9da0e53ac0521f19a01c619907887a0520762 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Fri, 26 Jun 2020 12:21:22 +0100
Subject: [PATCH 06/31] moved control connection creation to Metadata
---
src/Cassandra/Cluster.cs | 36 ++++--------------
src/Cassandra/Metadata.cs | 38 ++++++++++++++++---
.../Policies/LocalDatacenterProvider.cs | 4 +-
src/Cassandra/Requests/RequestExecution.cs | 10 ++---
.../SessionManagement/IInternalCluster.cs | 7 +---
5 files changed, 50 insertions(+), 45 deletions(-)
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index a9182e3f7..15c8e51ac 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -48,15 +48,13 @@ public class Cluster : IInternalCluster
private static ProtocolVersion _maxProtocolVersion = ProtocolVersion.MaxSupported;
internal static readonly Logger Logger = new Logger(typeof(Cluster));
private readonly CopyOnWriteList _connectedSessions = new CopyOnWriteList();
- private readonly IControlConnection _controlConnection;
private readonly SerializerManager _serializerManager;
+ private readonly bool _implicitContactPoint = false;
private volatile Exception _initException;
private readonly SemaphoreSlim _sessionCreateLock = new SemaphoreSlim(1, 1);
private long _sessionCounter = -1;
- private readonly bool _implicitContactPoint = false;
private readonly Metadata _metadata;
- private readonly IProtocolEventDebouncer _protocolEventDebouncer;
private IReadOnlyList _loadBalancingPolicies;
private readonly Task _initTask;
@@ -68,13 +66,7 @@ public class Cluster : IInternalCluster
private bool IsDisposed => Interlocked.Read(ref _state) == Cluster.Disposed;
ISerializerManager IInternalCluster.SerializerManager => _serializerManager;
-
- ///
- IControlConnection IInternalCluster.GetControlConnection()
- {
- return _controlConnection;
- }
-
+
Exception IInternalCluster.InitException => _initException;
///
@@ -162,11 +154,6 @@ private Cluster(IEnumerable contactPoints, Configuration configuration)
{
Configuration = configuration;
- _protocolEventDebouncer = new ProtocolEventDebouncer(
- configuration.TimerFactory,
- TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.RefreshSchemaDelayIncrement),
- TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.MaxTotalRefreshSchemaDelay));
-
var protocolVersion = _maxProtocolVersion;
if (Configuration.ProtocolOptions.MaxProtocolVersionValue != null &&
Configuration.ProtocolOptions.MaxProtocolVersionValue.Value.IsSupported(configuration))
@@ -174,6 +161,8 @@ private Cluster(IEnumerable contactPoints, Configuration configuration)
protocolVersion = Configuration.ProtocolOptions.MaxProtocolVersionValue.Value;
}
+ _serializerManager = new SerializerManager(protocolVersion, configuration.TypeSerializers);
+
var contactPointsList = contactPoints.ToList();
if (contactPointsList.Count == 0)
{
@@ -183,16 +172,8 @@ private Cluster(IEnumerable contactPoints, Configuration configuration)
}
var parsedContactPoints = configuration.ContactPointParser.ParseContactPoints(contactPointsList);
- _serializerManager = new SerializerManager(protocolVersion, configuration.TypeSerializers);
- _controlConnection = configuration.ControlConnectionFactory.Create(
- this,
- _protocolEventDebouncer,
- _serializerManager,
- Configuration,
- _metadata,
- parsedContactPoints);
-
- _metadata = new Metadata(configuration, _serializerManager, _controlConnection);
+
+ _metadata = new Metadata(this, configuration, _serializerManager, parsedContactPoints);
_initTask = Task.Run(InitAsync);
}
@@ -240,7 +221,7 @@ private async Task InitAsync()
// Only abort the async operations when at least twice the time for ConnectTimeout per host passed
var initialAbortTimeout = Configuration.SocketOptions.ConnectTimeoutMillis * 2 * _metadata.Hosts.Count;
initialAbortTimeout = Math.Max(initialAbortTimeout, Configuration.SocketOptions.MetadataAbortTimeout);
- var initTask = _controlConnection.InitAsync();
+ var initTask = _metadata.InitializeAsync();
try
{
await initTask.WaitToCompleteAsync(initialAbortTimeout).ConfigureAwait(false);
@@ -515,8 +496,7 @@ public async Task ShutdownAsync(int timeoutMs = Timeout.Infinite)
private async Task ShutdownInternalAsync()
{
- _controlConnection.Dispose();
- await _protocolEventDebouncer.ShutdownAsync().ConfigureAwait(false);
+ await _metadata.ShutdownAsync().ConfigureAwait(false);
Configuration.Timer.Dispose();
// Dispose policies
diff --git a/src/Cassandra/Metadata.cs b/src/Cassandra/Metadata.cs
index 131c20524..4039b7133 100644
--- a/src/Cassandra/Metadata.cs
+++ b/src/Cassandra/Metadata.cs
@@ -25,8 +25,10 @@
using Cassandra.Connections;
using Cassandra.Connections.Control;
using Cassandra.MetadataHelpers;
+using Cassandra.ProtocolEvents;
using Cassandra.Requests;
using Cassandra.Serialization;
+using Cassandra.SessionManagement;
using Cassandra.Tasks;
namespace Cassandra
@@ -44,6 +46,7 @@ public class Metadata
private volatile TokenMap _tokenMap;
private volatile ConcurrentDictionary _keyspaces = new ConcurrentDictionary();
private volatile ISchemaParser _schemaParser;
+ private readonly IProtocolEventDebouncer _protocolEventDebouncer;
private readonly int _queryAbortTimeout;
private volatile CopyOnWriteDictionary> _resolvedContactPoints =
new CopyOnWriteDictionary>();
@@ -56,7 +59,7 @@ public class Metadata
/// Returns the name of currently connected cluster.
///
/// the Cassandra name of currently connected cluster.
- public String ClusterName { get; internal set; }
+ public string ClusterName { get; internal set; }
///
/// Determines whether the cluster is provided as a service (DataStax Astra).
@@ -100,27 +103,52 @@ public class Metadata
internal IReadOnlyTokenMap TokenToReplicasMap => _tokenMap;
- internal Metadata(Configuration configuration, ISerializerManager serializerManager, IControlConnection controlConnection)
+ internal Metadata(IInternalCluster cluster, Configuration configuration, ISerializerManager serializerManager, IEnumerable parsedContactPoints)
{
_serializerManager = serializerManager;
_queryAbortTimeout = configuration.DefaultRequestOptions.QueryAbortTimeout;
Configuration = configuration;
Hosts = new Hosts();
+
+ _protocolEventDebouncer = new ProtocolEventDebouncer(
+ configuration.TimerFactory,
+ TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.RefreshSchemaDelayIncrement),
+ TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.MaxTotalRefreshSchemaDelay));
+
+ ControlConnection = configuration.ControlConnectionFactory.Create(
+ cluster,
+ _protocolEventDebouncer,
+ _serializerManager,
+ Configuration,
+ this,
+ parsedContactPoints);
+
Hosts.Down += OnHostDown;
Hosts.Up += OnHostUp;
- ControlConnection = controlConnection;
}
internal Metadata(
+ IInternalCluster cluster,
Configuration configuration,
ISerializerManager serializerManager,
- IControlConnection controlConnection,
+ IEnumerable contactPoints,
SchemaParser schemaParser)
- : this(configuration, serializerManager, controlConnection)
+ : this(cluster, configuration, serializerManager, contactPoints)
{
_schemaParser = schemaParser;
}
+ internal Task InitializeAsync()
+ {
+ return ControlConnection.InitAsync();
+ }
+
+ internal Task ShutdownAsync()
+ {
+ ControlConnection.Dispose();
+ return _protocolEventDebouncer.ShutdownAsync();
+ }
+
internal void SetResolvedContactPoints(IDictionary> resolvedContactPoints)
{
_resolvedContactPoints = new CopyOnWriteDictionary>(resolvedContactPoints);
diff --git a/src/Cassandra/Policies/LocalDatacenterProvider.cs b/src/Cassandra/Policies/LocalDatacenterProvider.cs
index fcad9f454..b35da2d98 100644
--- a/src/Cassandra/Policies/LocalDatacenterProvider.cs
+++ b/src/Cassandra/Policies/LocalDatacenterProvider.cs
@@ -26,6 +26,7 @@ internal class LocalDatacenterProvider : ILocalDatacenterProvider
private volatile bool _initialized = false;
private volatile IInternalCluster _cluster;
+ private volatile Metadata _metadata;
private volatile IEnumerable _availableDcs;
private volatile string _availableDcsStr;
private volatile string _cachedDatacenter;
@@ -33,6 +34,7 @@ internal class LocalDatacenterProvider : ILocalDatacenterProvider
public void Initialize(IInternalCluster cluster, Metadata metadata)
{
_cluster = cluster;
+ _metadata = metadata;
_availableDcs = metadata.AllHosts().Select(h => h.Datacenter).Where(dc => dc != null).Distinct().ToList();
_availableDcsStr = string.Join(", ", _availableDcs);
_initialized = true;
@@ -87,7 +89,7 @@ private string ValidateAndReturnDatacenter(string datacenter)
private string InferLocalDatacenter()
{
- var cc = _cluster.GetControlConnection();
+ var cc = _metadata.ControlConnection;
if (cc == null)
{
throw new DriverInternalError("ControlConnection was not correctly set");
diff --git a/src/Cassandra/Requests/RequestExecution.cs b/src/Cassandra/Requests/RequestExecution.cs
index 229ef4bf1..6f2dbce74 100644
--- a/src/Cassandra/Requests/RequestExecution.cs
+++ b/src/Cassandra/Requests/RequestExecution.cs
@@ -249,11 +249,11 @@ private void HandleSchemaChangeAsync(ResultResponse response, OutputSchemaChange
result.Info.SetSchemaInAgreement(schemaAgreed);
try
{
- await _session.InternalCluster.GetControlConnection()
- .HandleSchemaChangeEvent(schemaChange.SchemaChangeEventArgs, true)
- .WaitToCompleteAsync(
- _session.Cluster.Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds * 1000)
- .ConfigureAwait(false);
+ await _parent.Metadata.ControlConnection
+ .HandleSchemaChangeEvent(schemaChange.SchemaChangeEventArgs, true)
+ .WaitToCompleteAsync(
+ _session.Cluster.Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds * 1000)
+ .ConfigureAwait(false);
}
catch (TimeoutException)
{
diff --git a/src/Cassandra/SessionManagement/IInternalCluster.cs b/src/Cassandra/SessionManagement/IInternalCluster.cs
index 4c4820bfa..430d2dc52 100644
--- a/src/Cassandra/SessionManagement/IInternalCluster.cs
+++ b/src/Cassandra/SessionManagement/IInternalCluster.cs
@@ -31,12 +31,7 @@ internal interface IInternalCluster : ICluster
ISerializerManager SerializerManager { get; }
bool AnyOpenConnections(Host host);
-
- ///
- /// Gets the control connection used by the cluster
- ///
- IControlConnection GetControlConnection();
-
+
///
/// If contact points are not provided in the builder, the driver will use localhost
/// as an implicit contact point.
From 4baf9dc4579cfa8ba7d333257f218c946e62c63b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Fri, 26 Jun 2020 15:56:44 +0100
Subject: [PATCH 07/31] add ShutdownAsync to speculative exec policy
* Remove IDisposable implementation
* Move ControlConnection construction to Metadata
* Add Metadata InitializeAsync and ShutdownAsync
---
src/Cassandra/Cluster.cs | 2 +-
src/Cassandra/Metadata.cs | 6 +++++-
.../Policies/ConstantSpeculativeExecutionPolicy.cs | 12 ++++++------
.../Policies/ISpeculativeExecutionPolicy.cs | 10 +++++++---
.../Policies/NoSpeculativeExecutionPolicy.cs | 12 ++++++------
5 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 15c8e51ac..99caa96e8 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -508,7 +508,7 @@ private async Task ShutdownInternalAsync()
foreach (var sep in speculativeExecutionPolicies)
{
- sep.Dispose();
+ await sep.ShutdownAsync().ConfigureAwait(false);
}
}
diff --git a/src/Cassandra/Metadata.cs b/src/Cassandra/Metadata.cs
index 4039b7133..366b132b0 100644
--- a/src/Cassandra/Metadata.cs
+++ b/src/Cassandra/Metadata.cs
@@ -103,7 +103,11 @@ public class Metadata
internal IReadOnlyTokenMap TokenToReplicasMap => _tokenMap;
- internal Metadata(IInternalCluster cluster, Configuration configuration, ISerializerManager serializerManager, IEnumerable parsedContactPoints)
+ internal Metadata(
+ IInternalCluster cluster,
+ Configuration configuration,
+ ISerializerManager serializerManager,
+ IEnumerable parsedContactPoints)
{
_serializerManager = serializerManager;
_queryAbortTimeout = configuration.DefaultRequestOptions.QueryAbortTimeout;
diff --git a/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
index 818ea220d..03831dea2 100644
--- a/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
@@ -50,12 +50,7 @@ public ConstantSpeculativeExecutionPolicy(long delay, int maxSpeculativeExecutio
public long Delay { get; }
public int MaxSpeculativeExecutions { get; }
-
- public void Dispose()
- {
-
- }
-
+
public Task InitializeAsync(Metadata metadata)
{
return TaskHelper.Completed;
@@ -66,6 +61,11 @@ public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
return new ConstantSpeculativeExecutionPlan(Delay, MaxSpeculativeExecutions);
}
+ public Task ShutdownAsync()
+ {
+ return TaskHelper.Completed;
+ }
+
private class ConstantSpeculativeExecutionPlan : ISpeculativeExecutionPlan
{
private readonly long _delay;
diff --git a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
index 96f2341af..065c47890 100644
--- a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
@@ -14,7 +14,6 @@
// limitations under the License.
//
-using System;
using System.Threading.Tasks;
// ReSharper disable once CheckNamespace
@@ -24,7 +23,7 @@ namespace Cassandra
/// The policy that decides if the driver will send speculative queries to the next hosts when the current host takes too long to respond.
/// only idempotent statements will be speculatively retried, see for more information.
///
- public interface ISpeculativeExecutionPolicy : IDisposable
+ public interface ISpeculativeExecutionPolicy
{
///
/// Initializes the policy at cluster startup.
@@ -38,5 +37,10 @@ public interface ISpeculativeExecutionPolicy : IDisposable
/// the query for which to build a plan.
///
ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement);
+
+ ///
+ /// Disposes the policy at cluster shutdown.
+ ///
+ Task ShutdownAsync();
}
-}
+}
\ No newline at end of file
diff --git a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
index a6f5dbaef..3717ee358 100644
--- a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
@@ -32,12 +32,7 @@ private NoSpeculativeExecutionPolicy()
{
}
-
- public void Dispose()
- {
-
- }
-
+
public Task InitializeAsync(Metadata metadata)
{
return TaskHelper.Completed;
@@ -48,6 +43,11 @@ public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
return Plan;
}
+ public Task ShutdownAsync()
+ {
+ return TaskHelper.Completed;
+ }
+
private class NoSpeculativeExecutionPlan : ISpeculativeExecutionPlan
{
public long NextExecution(Host lastQueried)
From 5dc87e526082d84b73c513f9d1d7d541bb48b3ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Mon, 29 Jun 2020 17:49:55 +0100
Subject: [PATCH 08/31] introduce metadata internal and make metadata lazy
(wip)
---
src/Cassandra/Cluster.cs | 66 +-
src/Cassandra/ClusterDescription.cs | 48 ++
.../Connections/Control/ControlConnection.cs | 53 +-
.../Control/ControlConnectionFactory.cs | 2 +-
.../Control/IControlConnectionFactory.cs | 2 +-
.../Connections/Control/IInternalMetadata.cs | 228 ++++++
.../Control/IProtocolVersionNegotiator.cs | 11 +-
.../ISupportedOptionsInitializerFactory.cs | 2 +-
.../Connections/Control/ITopologyRefresher.cs | 2 +-
.../Control/ITopologyRefresherFactory.cs | 2 +-
.../Connections/Control/InternalMetadata.cs | 685 ++++++++++++++++++
.../Control/ProtocolVersionNegotiator.cs | 4 +-
.../Control/SupportedOptionsInitializer.cs | 8 +-
.../SupportedOptionsInitializerFactory.cs | 4 +-
.../Connections/Control/TopologyRefresher.cs | 56 +-
.../Control/TopologyRefresherFactory.cs | 4 +-
.../Insights/IInsightsSupportVerifier.cs | 3 +-
.../Insights/InsightsSupportVerifier.cs | 5 +-
src/Cassandra/ICluster.cs | 20 +-
src/Cassandra/KeyspaceMetadata.cs | 64 +-
src/Cassandra/Metadata.cs | 663 +++++------------
.../MetadataHelpers/ISchemaParserFactory.cs | 3 +-
.../Policies/ILocalDatacenterProvider.cs | 3 +-
.../Policies/LocalDatacenterProvider.cs | 12 +-
src/Cassandra/Requests/IPrepareHandler.cs | 2 +-
src/Cassandra/Requests/PrepareHandler.cs | 28 +-
.../SessionManagement/IInternalCluster.cs | 6 +-
27 files changed, 1361 insertions(+), 625 deletions(-)
create mode 100644 src/Cassandra/ClusterDescription.cs
create mode 100644 src/Cassandra/Connections/Control/IInternalMetadata.cs
create mode 100644 src/Cassandra/Connections/Control/InternalMetadata.cs
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 99caa96e8..503f46ad0 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -54,14 +54,16 @@ public class Cluster : IInternalCluster
private readonly SemaphoreSlim _sessionCreateLock = new SemaphoreSlim(1, 1);
private long _sessionCounter = -1;
- private readonly Metadata _metadata;
+ private readonly IInternalMetadata _internalMetadata;
private IReadOnlyList _loadBalancingPolicies;
- private readonly Task _initTask;
+ private readonly Task _initTask;
private long _state = Cluster.Initializing;
internal IInternalCluster InternalRef => this;
+
+ IInternalMetadata IInternalCluster.InternalMetadata => _internalMetadata;
private bool IsDisposed => Interlocked.Read(ref _state) == Cluster.Disposed;
@@ -142,14 +144,8 @@ public static int MaxProtocolVersion
bool IInternalCluster.ImplicitContactPoint => _implicitContactPoint;
///
- public Metadata Metadata => TaskHelper.WaitToComplete(GetMetadataAsync());
-
- ///
- public Task GetMetadataAsync()
- {
- return InternalRef.TryInitAndGetMetadataAsync();
- }
-
+ public Metadata Metadata { get; }
+
private Cluster(IEnumerable contactPoints, Configuration configuration)
{
Configuration = configuration;
@@ -173,13 +169,14 @@ private Cluster(IEnumerable contactPoints, Configuration configuration)
var parsedContactPoints = configuration.ContactPointParser.ParseContactPoints(contactPointsList);
- _metadata = new Metadata(this, configuration, _serializerManager, parsedContactPoints);
+ Metadata = new Metadata(this, configuration, _serializerManager, parsedContactPoints);
+ _internalMetadata = Metadata.InternalMetadata;
_initTask = Task.Run(InitAsync);
}
///
- Task IInternalCluster.TryInitAndGetMetadataAsync()
+ Task IInternalCluster.TryInitializeAsync()
{
var currentState = Interlocked.Read(ref _state);
if (currentState == Cluster.Initialized)
@@ -202,7 +199,7 @@ Task IInternalCluster.TryInitAndGetMetadataAsync()
return _initTask;
}
- private async Task InitAsync()
+ private async Task InitAsync()
{
Cluster.Logger.Info("Connecting to cluster using {0}", Cluster.GetAssemblyInfo());
try
@@ -219,9 +216,9 @@ private async Task InitAsync()
_loadBalancingPolicies = loadBalancingPolicies.ToList();
// Only abort the async operations when at least twice the time for ConnectTimeout per host passed
- var initialAbortTimeout = Configuration.SocketOptions.ConnectTimeoutMillis * 2 * _metadata.Hosts.Count;
+ var initialAbortTimeout = Configuration.SocketOptions.ConnectTimeoutMillis * 2 * _internalMetadata.Hosts.Count;
initialAbortTimeout = Math.Max(initialAbortTimeout, Configuration.SocketOptions.MetadataAbortTimeout);
- var initTask = _metadata.InitializeAsync();
+ var initTask = Metadata.TryInitializeAsync();
try
{
await initTask.WaitToCompleteAsync(initialAbortTimeout).ConfigureAwait(false);
@@ -253,17 +250,17 @@ private async Task InitAsync()
}
// initialize the local datacenter provider
- Configuration.LocalDatacenterProvider.Initialize(this, _metadata);
+ Configuration.LocalDatacenterProvider.Initialize(this, _internalMetadata);
// Initialize policies
foreach (var lbp in loadBalancingPolicies)
{
- await lbp.InitializeAsync(_metadata).ConfigureAwait(false);
+ await lbp.InitializeAsync(Metadata).ConfigureAwait(false);
}
foreach (var sep in speculativeExecutionPolicies)
{
- await sep.InitializeAsync(_metadata).ConfigureAwait(false);
+ await sep.InitializeAsync(Metadata).ConfigureAwait(false);
}
InitializeHostDistances();
@@ -272,12 +269,10 @@ private async Task InitAsync()
SetMetadataDependentOptions();
Cluster.Logger.Info("Cluster Connected using binary protocol version: [" + _serializerManager.CurrentProtocolVersion + "]");
- _metadata.Hosts.Added += _metadata.OnHostAdded;
- _metadata.Hosts.Removed += _metadata.OnHostRemoved;
- _metadata.Hosts.Up += OnHostUp;
- Cluster.Logger.Info("Cluster [" + _metadata.ClusterName + "] has been initialized.");
-
- return _metadata;
+ _internalMetadata.Hosts.Added += _internalMetadata.OnHostAdded;
+ _internalMetadata.Hosts.Removed += _internalMetadata.OnHostRemoved;
+ _internalMetadata.Hosts.Up += OnHostUp;
+ Cluster.Logger.Info("Cluster [" + _internalMetadata.ClusterName + "] has been initialized.");
}
catch (NoHostAvailableException)
{
@@ -300,7 +295,7 @@ private async Task InitAsync()
private void InitializeHostDistances()
{
- foreach (var host in _metadata.AllHosts())
+ foreach (var host in _internalMetadata.AllHosts())
{
InternalRef.RetrieveAndSetDistance(host);
}
@@ -315,7 +310,7 @@ private static string GetAssemblyInfo()
IReadOnlyDictionary> IInternalCluster.GetResolvedEndpoints()
{
- return _metadata.ResolvedContactPoints;
+ return _internalMetadata.ResolvedContactPoints;
}
///
@@ -393,7 +388,7 @@ private long GetAndIncrementSessionCounter()
private void SetMetadataDependentOptions()
{
- if (_metadata.IsDbaas)
+ if (_internalMetadata.IsDbaas)
{
Configuration.SetDefaultConsistencyLevel(ConsistencyLevel.LocalQuorum);
}
@@ -491,12 +486,12 @@ public async Task ShutdownAsync(int timeoutMs = Timeout.Infinite)
await ShutdownInternalAsync().ConfigureAwait(false);
- Cluster.Logger.Info("Cluster [" + _metadata.ClusterName + "] has been shut down.");
+ Cluster.Logger.Info("Cluster [" + _internalMetadata.ClusterName + "] has been shut down.");
}
private async Task ShutdownInternalAsync()
{
- await _metadata.ShutdownAsync().ConfigureAwait(false);
+ await Metadata.ShutdownAsync().ConfigureAwait(false);
Configuration.Timer.Dispose();
// Dispose policies
@@ -535,9 +530,9 @@ HostDistance IInternalCluster.RetrieveAndSetDistance(Host host)
async Task IInternalCluster.PrepareAsync(
IInternalSession session, string cqlQuery, string keyspace, IDictionary customPayload)
{
- var metadata = await InternalRef.TryInitAndGetMetadataAsync().ConfigureAwait(false);
+ await InternalRef.TryInitializeAsync().ConfigureAwait(false);
- var serializer = metadata.SerializerManager.GetCurrentSerializer();
+ var serializer = _internalMetadata.SerializerManager.GetCurrentSerializer();
var currentVersion = serializer.ProtocolVersion;
if (!currentVersion.SupportsKeyspaceInRequest() && keyspace != null)
{
@@ -548,18 +543,17 @@ async Task IInternalCluster.PrepareAsync(
}
var request = new PrepareRequest(serializer, cqlQuery, keyspace, customPayload);
- return await PrepareAsync(session, metadata, request).ConfigureAwait(false);
+ return await PrepareAsync(session, request).ConfigureAwait(false);
}
- private async Task PrepareAsync(IInternalSession session, Metadata metadata, PrepareRequest request)
+ private async Task PrepareAsync(IInternalSession session, PrepareRequest request)
{
var lbp = session.Cluster.Configuration.DefaultRequestOptions.LoadBalancingPolicy;
var handler = InternalRef.Configuration.PrepareHandlerFactory.CreatePrepareHandler(_serializerManager, this);
var ps = await handler.PrepareAsync(
request,
- session,
- metadata,
- lbp.NewQueryPlan(_metadata, session.Keyspace, null).GetEnumerator()).ConfigureAwait(false);
+ session,
+ lbp.NewQueryPlan(Metadata, session.Keyspace, null).GetEnumerator()).ConfigureAwait(false);
var psAdded = InternalRef.PreparedQueries.GetOrAdd(ps.Id, ps);
if (ps != psAdded)
{
diff --git a/src/Cassandra/ClusterDescription.cs b/src/Cassandra/ClusterDescription.cs
new file mode 100644
index 000000000..7cf4b4e52
--- /dev/null
+++ b/src/Cassandra/ClusterDescription.cs
@@ -0,0 +1,48 @@
+//
+// Copyright (C) DataStax Inc.
+//
+// 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.
+//
+
+namespace Cassandra
+{
+ ///
+ /// Provides a snapshot of cluster metadata properties.
+ ///
+ public class ClusterDescription
+ {
+ public ClusterDescription(string clusterName, bool isDbaas, ProtocolVersion protocolVersion)
+ {
+ ClusterName = clusterName;
+ IsDbaas = isDbaas;
+ ProtocolVersion = protocolVersion;
+ }
+
+ ///
+ /// Returns the name of currently connected cluster.
+ ///
+ /// the Cassandra name of currently connected cluster.
+ public string ClusterName { get; }
+
+ ///
+ /// Determines whether the cluster is provided as a service (DataStax Astra).
+ ///
+ public bool IsDbaas { get; }
+
+ ///
+ /// Gets the Cassandra native binary protocol version that was
+ /// negotiated between the driver and the Cassandra node.
+ ///
+ public ProtocolVersion ProtocolVersion { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/ControlConnection.cs b/src/Cassandra/Connections/Control/ControlConnection.cs
index 2026d59c6..6e9b08cf5 100644
--- a/src/Cassandra/Connections/Control/ControlConnection.cs
+++ b/src/Cassandra/Connections/Control/ControlConnection.cs
@@ -33,7 +33,7 @@ namespace Cassandra.Connections.Control
internal class ControlConnection : IControlConnection
{
private readonly IInternalCluster _cluster;
- private readonly Metadata _metadata;
+ private readonly IInternalMetadata _internalMetadata;
private volatile Host _host;
private volatile IConnectionEndPoint _currentConnectionEndPoint;
private volatile IConnection _connection;
@@ -71,11 +71,11 @@ internal ControlConnection(
IProtocolEventDebouncer eventDebouncer,
ISerializerManager serializerManager,
Configuration config,
- Metadata metadata,
+ IInternalMetadata internalMetadata,
IEnumerable contactPoints)
{
_cluster = cluster;
- _metadata = metadata;
+ _internalMetadata = internalMetadata;
_reconnectionPolicy = config.Policies.ReconnectionPolicy;
_reconnectionSchedule = _reconnectionPolicy.NewSchedule();
_reconnectionTimer = new Timer(ReconnectEventHandler, null, Timeout.Infinite, Timeout.Infinite);
@@ -83,8 +83,8 @@ internal ControlConnection(
_serializer = serializerManager;
_eventDebouncer = eventDebouncer;
_contactPoints = contactPoints;
- _topologyRefresher = config.TopologyRefresherFactory.Create(metadata, config);
- _supportedOptionsInitializer = config.SupportedOptionsInitializerFactory.Create(metadata);
+ _topologyRefresher = config.TopologyRefresherFactory.Create(internalMetadata, config);
+ _supportedOptionsInitializer = config.SupportedOptionsInitializerFactory.Create(internalMetadata);
if (!_config.KeepContactPointsUnresolved)
{
@@ -106,7 +106,7 @@ public async Task InitAsync()
///
/// Resolves the contact points to a read only list of which will be used
- /// during initialization. Also sets .
+ /// during initialization. Also sets .
///
private async Task InitialContactPointResolutionAsync()
{
@@ -116,7 +116,7 @@ private async Task InitialContactPointResolutionAsync()
var resolvedContactPoints = tasksDictionary.ToDictionary(t => t.Key, t => t.Value.Result);
- _metadata.SetResolvedContactPoints(resolvedContactPoints);
+ _internalMetadata.SetResolvedContactPoints(resolvedContactPoints);
if (!resolvedContactPoints.Any(kvp => kvp.Value.Any()))
{
@@ -127,7 +127,7 @@ private async Task InitialContactPointResolutionAsync()
private bool TotalConnectivityLoss()
{
- var currentHosts = _metadata.AllHosts();
+ var currentHosts = _internalMetadata.AllHosts();
return currentHosts.Count(h => h.IsUp) == 0 || currentHosts.All(h => !_cluster.AnyOpenConnections(h));
}
@@ -143,7 +143,7 @@ private async Task> ResolveContactPoint(IContac
var endpoints = await contactPoint.GetConnectionEndPointsAsync(
_config.KeepContactPointsUnresolved || connectivityLoss).ConfigureAwait(false);
- return _metadata.UpdateResolvedContactPoint(contactPoint, endpoints);
+ return _internalMetadata.UpdateResolvedContactPoint(contactPoint, endpoints);
}
private async Task> ResolveHostContactPointOrConnectionEndpointAsync(
@@ -194,12 +194,11 @@ private IEnumerable>> AllHostsEndPointReso
}
private IEnumerable>> DefaultLbpHostsEnumerable(
- Metadata metadata,
ConcurrentDictionary attemptedContactPoints,
ConcurrentDictionary attemptedHosts,
bool isInitializing)
{
- foreach (var host in _config.DefaultRequestOptions.LoadBalancingPolicy.NewQueryPlan(metadata, null, null))
+ foreach (var host in _config.DefaultRequestOptions.LoadBalancingPolicy.NewQueryPlan(_cluster.Metadata, null, null))
{
if (attemptedHosts.TryAdd(host, null))
{
@@ -257,7 +256,7 @@ private async Task Connect(bool isInitializing)
// start with endpoints from the default LBP if it is already initialized
if (!isInitializing)
{
- endPointResolutionTasksLazyIterator = DefaultLbpHostsEnumerable(_metadata, attemptedContactPoints, attemptedHosts, isInitializing);
+ endPointResolutionTasksLazyIterator = DefaultLbpHostsEnumerable(attemptedContactPoints, attemptedHosts, isInitializing);
}
// add contact points next
@@ -340,11 +339,11 @@ await _config.ProtocolVersionNegotiator.ChangeProtocolVersion(
if (isInitializing)
{
await _config.ProtocolVersionNegotiator.NegotiateVersionAsync(
- _config, _metadata, connection, _serializer).ConfigureAwait(false);
+ _config, _internalMetadata, connection, _serializer).ConfigureAwait(false);
}
await _config.ServerEventsSubscriber.SubscribeToServerEvents(connection, OnConnectionCassandraEvent).ConfigureAwait(false);
- await _metadata.RebuildTokenMapAsync(false, _config.MetadataSyncOptions.MetadataSyncEnabled).ConfigureAwait(false);
+ await _internalMetadata.RebuildTokenMapAsync(false, _config.MetadataSyncOptions.MetadataSyncEnabled).ConfigureAwait(false);
_host.Down += OnHostDown;
@@ -472,7 +471,7 @@ private async Task Refresh()
SetCurrentConnection(currentHost, currentEndPoint);
- await _metadata.RebuildTokenMapAsync(false, _config.MetadataSyncOptions.MetadataSyncEnabled).ConfigureAwait(false);
+ await _internalMetadata.RebuildTokenMapAsync(false, _config.MetadataSyncOptions.MetadataSyncEnabled).ConfigureAwait(false);
_reconnectionSchedule = _reconnectionPolicy.NewSchedule();
}
catch (SocketException ex)
@@ -577,18 +576,18 @@ public Task HandleSchemaChangeEvent(SchemaChangeEventArgs ssc, bool processNow)
handler = () =>
{
//Can be either a table or a view
- _metadata.ClearTable(ssc.Keyspace, ssc.Table);
- _metadata.ClearView(ssc.Keyspace, ssc.Table);
+ _internalMetadata.ClearTable(ssc.Keyspace, ssc.Table);
+ _internalMetadata.ClearView(ssc.Keyspace, ssc.Table);
return TaskHelper.Completed;
};
}
else if (ssc.FunctionName != null)
{
- handler = TaskHelper.ActionToAsync(() => _metadata.ClearFunction(ssc.Keyspace, ssc.FunctionName, ssc.Signature));
+ handler = TaskHelper.ActionToAsync(() => _internalMetadata.ClearFunction(ssc.Keyspace, ssc.FunctionName, ssc.Signature));
}
else if (ssc.AggregateName != null)
{
- handler = TaskHelper.ActionToAsync(() => _metadata.ClearAggregate(ssc.Keyspace, ssc.AggregateName, ssc.Signature));
+ handler = TaskHelper.ActionToAsync(() => _internalMetadata.ClearAggregate(ssc.Keyspace, ssc.AggregateName, ssc.Signature));
}
else if (ssc.Type != null)
{
@@ -596,7 +595,7 @@ public Task HandleSchemaChangeEvent(SchemaChangeEventArgs ssc, bool processNow)
}
else if (ssc.What == SchemaChangeEventArgs.Reason.Dropped)
{
- handler = TaskHelper.ActionToAsync(() => _metadata.RemoveKeyspace(ssc.Keyspace));
+ handler = TaskHelper.ActionToAsync(() => _internalMetadata.RemoveKeyspace(ssc.Keyspace));
}
else
{
@@ -611,7 +610,7 @@ private void HandleStatusChangeEvent(StatusChangeEventArgs e)
//The address in the Cassandra event message needs to be translated
var address = TranslateAddress(e.Address);
ControlConnection.Logger.Info("Received Node status change event: host {0} is {1}", address, e.What.ToString().ToUpper());
- if (!_metadata.Hosts.TryGet(address, out var host))
+ if (!_internalMetadata.Hosts.TryGet(address, out var host))
{
ControlConnection.Logger.Info("Received status change event for host {0} but it was not found", address);
return;
@@ -640,7 +639,7 @@ private void SetCurrentConnection(Host host, IConnectionEndPoint endPoint)
{
_host = host;
_currentConnectionEndPoint = endPoint;
- _metadata.SetCassandraVersion(host.CassandraVersion);
+ _internalMetadata.SetCassandraVersion(host.CassandraVersion);
}
///
@@ -694,12 +693,12 @@ public Task UnsafeSendQueryRequestAsync(string cqlQuery, QueryProtocol
private IEnumerable GetHostEnumerable()
{
var index = 0;
- var hosts = _metadata.Hosts.ToArray();
+ var hosts = _internalMetadata.Hosts.ToArray();
while (index < hosts.Length)
{
yield return hosts[index++];
// Check that the collection changed
- var newHosts = _metadata.Hosts.ToCollection();
+ var newHosts = _internalMetadata.Hosts.ToCollection();
if (newHosts.Count != hosts.Length)
{
index = 0;
@@ -713,7 +712,7 @@ public async Task HandleKeyspaceRefreshLaterAsync(string keyspace)
{
var @event = new KeyspaceProtocolEvent(true, keyspace, async () =>
{
- await _metadata.RefreshSingleKeyspace(keyspace).ConfigureAwait(false);
+ await _internalMetadata.RefreshSingleKeyspaceAsync(keyspace).ConfigureAwait(false);
});
await _eventDebouncer.HandleEventAsync(@event, false).ConfigureAwait(false);
}
@@ -721,7 +720,7 @@ public async Task HandleKeyspaceRefreshLaterAsync(string keyspace)
///
public Task ScheduleKeyspaceRefreshAsync(string keyspace, bool processNow)
{
- var @event = new KeyspaceProtocolEvent(true, keyspace, () => _metadata.RefreshSingleKeyspace(keyspace));
+ var @event = new KeyspaceProtocolEvent(true, keyspace, () => _internalMetadata.RefreshSingleKeyspaceAsync(keyspace));
return processNow
? _eventDebouncer.HandleEventAsync(@event, true)
: _eventDebouncer.ScheduleEventAsync(@event, false);
@@ -743,7 +742,7 @@ private Task ScheduleHostsRefreshAsync()
///
public Task ScheduleAllKeyspacesRefreshAsync(bool processNow)
{
- var @event = new ProtocolEvent(() => _metadata.RebuildTokenMapAsync(false, true));
+ var @event = new ProtocolEvent(() => _internalMetadata.RebuildTokenMapAsync(false, true));
return processNow
? _eventDebouncer.HandleEventAsync(@event, true)
: _eventDebouncer.ScheduleEventAsync(@event, false);
diff --git a/src/Cassandra/Connections/Control/ControlConnectionFactory.cs b/src/Cassandra/Connections/Control/ControlConnectionFactory.cs
index ef3b3191f..1f43b08d7 100644
--- a/src/Cassandra/Connections/Control/ControlConnectionFactory.cs
+++ b/src/Cassandra/Connections/Control/ControlConnectionFactory.cs
@@ -28,7 +28,7 @@ public IControlConnection Create(
IProtocolEventDebouncer protocolEventDebouncer,
ISerializerManager serializerManager,
Configuration config,
- Metadata metadata,
+ IInternalMetadata metadata,
IEnumerable contactPoints)
{
return new ControlConnection(
diff --git a/src/Cassandra/Connections/Control/IControlConnectionFactory.cs b/src/Cassandra/Connections/Control/IControlConnectionFactory.cs
index 648483c70..a501f03d2 100644
--- a/src/Cassandra/Connections/Control/IControlConnectionFactory.cs
+++ b/src/Cassandra/Connections/Control/IControlConnectionFactory.cs
@@ -28,7 +28,7 @@ IControlConnection Create(
IProtocolEventDebouncer protocolEventDebouncer,
ISerializerManager serializerManager,
Configuration config,
- Metadata metadata,
+ IInternalMetadata internalMetadata,
IEnumerable contactPoints);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/IInternalMetadata.cs b/src/Cassandra/Connections/Control/IInternalMetadata.cs
new file mode 100644
index 000000000..b9ec24d5d
--- /dev/null
+++ b/src/Cassandra/Connections/Control/IInternalMetadata.cs
@@ -0,0 +1,228 @@
+//
+// Copyright (C) DataStax Inc.
+//
+// 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.
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Threading.Tasks;
+using Cassandra.MetadataHelpers;
+using Cassandra.Serialization;
+
+namespace Cassandra.Connections.Control
+{
+ internal interface IInternalMetadata
+ {
+ ///
+ /// Gets the configuration associated with this instance.
+ ///
+ Configuration Configuration { get; }
+
+ ///
+ /// Control connection to be used to execute the queries to retrieve the metadata
+ ///
+ IControlConnection ControlConnection { get; }
+
+ ISerializerManager SerializerManager { get; }
+
+ ISchemaParser SchemaParser { get; }
+
+ string Partitioner { get; set; }
+
+ Hosts Hosts { get; }
+
+ IReadOnlyDictionary> ResolvedContactPoints { get; }
+
+ IReadOnlyTokenMap TokenToReplicasMap { get; }
+
+ bool IsDbaas { get; }
+
+ string ClusterName { get; }
+
+ ProtocolVersion ProtocolVersion { get; }
+
+ KeyValuePair[] KeyspacesSnapshot { get; }
+
+ Task InitAsync();
+
+ Task ShutdownAsync();
+
+ void SetResolvedContactPoints(
+ IDictionary> resolvedContactPoints);
+
+ void OnHostRemoved(Host h);
+
+ void OnHostAdded(Host h);
+
+ Host GetHost(IPEndPoint address);
+
+ Host AddHost(IPEndPoint address);
+
+ Host AddHost(IPEndPoint address, IContactPoint contactPoint);
+
+ void RemoveHost(IPEndPoint address);
+
+ void FireSchemaChangedEvent(SchemaChangedEventArgs.Kind what, string keyspace, string table, object sender = null);
+
+ void OnHostDown(Host h);
+
+ void OnHostUp(Host h);
+
+ ///
+ /// Returns all known hosts of this cluster.
+ ///
+ /// collection of all known hosts of this cluster.
+ ICollection AllHosts();
+
+ IEnumerable AllReplicas();
+
+ Task RebuildTokenMapAsync(bool retry, bool fetchKeyspaces);
+
+ ///
+ /// this method should be called by the event debouncer
+ ///
+ bool RemoveKeyspaceFromTokenMap(string name);
+
+ Task UpdateTokenMapForKeyspace(string name);
+
+ ///
+ /// Get the replicas for a given partition key and keyspace
+ ///
+ ICollection GetReplicas(string keyspaceName, byte[] partitionKey);
+
+ ///
+ /// Get the replicas for a given partition key
+ ///
+ ICollection GetReplicas(byte[] partitionKey);
+
+ ///
+ /// Returns metadata of specified keyspace.
+ ///
+ /// the name of the keyspace for which metadata should be
+ /// returned.
+ /// the metadata of the requested keyspace or null if
+ /// * keyspace is not a known keyspace.
+ Task GetKeyspaceAsync(string keyspace);
+
+ ///
+ /// Returns a collection of all defined keyspaces names.
+ ///
+ /// a collection of all defined keyspaces names.
+ Task> GetKeyspacesAsync();
+
+ ///
+ /// Returns names of all tables which are defined within specified keyspace.
+ ///
+ /// the name of the keyspace for which all tables metadata should be
+ /// returned.
+ /// an ICollection of the metadata for the tables defined in this
+ /// keyspace.
+ Task> GetTablesAsync(string keyspace);
+
+ ///
+ /// Returns TableMetadata for specified table in specified keyspace.
+ ///
+ /// name of the keyspace within specified table is defined.
+ /// name of table for which metadata should be returned.
+ /// a TableMetadata for the specified table in the specified keyspace.
+ Task GetTableAsync(string keyspace, string tableName);
+
+ ///
+ /// Returns the view metadata for the provided view name in the keyspace.
+ ///
+ /// name of the keyspace within specified view is defined.
+ /// name of view.
+ /// a MaterializedViewMetadata for the view in the specified keyspace.
+ Task GetMaterializedViewAsync(string keyspace, string name);
+
+ ///
+ /// Gets the definition associated with a User Defined Type from Cassandra
+ ///
+ Task GetUdtDefinitionAsync(string keyspace, string typeName);
+
+ ///
+ /// Gets the definition associated with a User Defined Function from Cassandra
+ ///
+ /// The function metadata or null if not found.
+ Task GetFunctionAsync(string keyspace, string name, string[] signature);
+
+ ///
+ /// Gets the definition associated with a aggregate from Cassandra
+ ///
+ /// The aggregate metadata or null if not found.
+ Task GetAggregateAsync(string keyspace, string name, string[] signature);
+
+ ///
+ /// Gets the query trace.
+ ///
+ /// The query trace that contains the id, which properties are going to be populated.
+ ///
+ Task GetQueryTraceAsync(QueryTrace trace);
+
+ ///
+ /// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
+ /// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
+ ///
+ Task RefreshSchemaAsync(string keyspace = null, string table = null);
+
+ ///
+ /// this method should be called by the event debouncer
+ ///
+ bool RemoveKeyspace(string name);
+
+ ///
+ /// this method should be called by the event debouncer
+ ///
+ Task RefreshSingleKeyspaceAsync(string name);
+
+ void ClearTable(string keyspaceName, string tableName);
+
+ void ClearView(string keyspaceName, string name);
+
+ void ClearFunction(string keyspaceName, string functionName, string[] signature);
+
+ void ClearAggregate(string keyspaceName, string aggregateName, string[] signature);
+
+ ///
+ /// Initiates a schema agreement check.
+ ///
+ /// Schema changes need to be propagated to all nodes in the cluster.
+ /// Once they have settled on a common version, we say that they are in agreement.
+ ///
+ /// This method does not perform retries so
+ /// does not apply.
+ ///
+ /// True if schema agreement was successful and false if it was not successful.
+ Task CheckSchemaAgreementAsync();
+
+ ///
+ /// Waits until that the schema version in all nodes is the same or the waiting time passed.
+ /// This method blocks the calling thread.
+ ///
+ Task WaitForSchemaAgreementAsync(IConnection connection);
+
+ ///
+ /// Sets the Cassandra version in order to identify how to parse the metadata information
+ ///
+ ///
+ void SetCassandraVersion(Version version);
+
+ void SetProductTypeAsDbaas();
+
+ void SetClusterName(string clusterName);
+
+ IEnumerable UpdateResolvedContactPoint(
+ IContactPoint contactPoint, IEnumerable endpoints);
+ }
+}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/IProtocolVersionNegotiator.cs b/src/Cassandra/Connections/Control/IProtocolVersionNegotiator.cs
index 12938263e..41c26db50 100644
--- a/src/Cassandra/Connections/Control/IProtocolVersionNegotiator.cs
+++ b/src/Cassandra/Connections/Control/IProtocolVersionNegotiator.cs
@@ -20,8 +20,15 @@ namespace Cassandra.Connections.Control
{
internal interface IProtocolVersionNegotiator
{
- Task ChangeProtocolVersion(Configuration config, ISerializerManager serializer, ProtocolVersion nextVersion, IConnection previousConnection, UnsupportedProtocolVersionException ex = null, ProtocolVersion? previousVersion = null);
+ Task ChangeProtocolVersion(
+ Configuration config,
+ ISerializerManager serializer,
+ ProtocolVersion nextVersion,
+ IConnection previousConnection,
+ UnsupportedProtocolVersionException ex = null,
+ ProtocolVersion? previousVersion = null);
- Task NegotiateVersionAsync(Configuration config, Metadata metadata, IConnection connection, ISerializerManager serializer);
+ Task NegotiateVersionAsync(
+ Configuration config, IInternalMetadata internalMetadata, IConnection connection, ISerializerManager serializer);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/ISupportedOptionsInitializerFactory.cs b/src/Cassandra/Connections/Control/ISupportedOptionsInitializerFactory.cs
index d5610f5a7..5950b2b8e 100644
--- a/src/Cassandra/Connections/Control/ISupportedOptionsInitializerFactory.cs
+++ b/src/Cassandra/Connections/Control/ISupportedOptionsInitializerFactory.cs
@@ -17,6 +17,6 @@ namespace Cassandra.Connections.Control
{
internal interface ISupportedOptionsInitializerFactory
{
- ISupportedOptionsInitializer Create(Metadata metadata);
+ ISupportedOptionsInitializer Create(IInternalMetadata internalMetadata);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/ITopologyRefresher.cs b/src/Cassandra/Connections/Control/ITopologyRefresher.cs
index 2f5b7d176..acdd3e56f 100644
--- a/src/Cassandra/Connections/Control/ITopologyRefresher.cs
+++ b/src/Cassandra/Connections/Control/ITopologyRefresher.cs
@@ -19,7 +19,7 @@
namespace Cassandra.Connections.Control
{
///
- /// Class that issues system table queries and updates the hosts collection on .
+ /// Class that issues system table queries and updates the hosts collection on .
///
internal interface ITopologyRefresher
{
diff --git a/src/Cassandra/Connections/Control/ITopologyRefresherFactory.cs b/src/Cassandra/Connections/Control/ITopologyRefresherFactory.cs
index 72c1feb32..b6fd2518c 100644
--- a/src/Cassandra/Connections/Control/ITopologyRefresherFactory.cs
+++ b/src/Cassandra/Connections/Control/ITopologyRefresherFactory.cs
@@ -17,6 +17,6 @@ namespace Cassandra.Connections.Control
{
internal interface ITopologyRefresherFactory
{
- ITopologyRefresher Create(Metadata metadata, Configuration config);
+ ITopologyRefresher Create(IInternalMetadata internalMetadata, Configuration config);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/InternalMetadata.cs b/src/Cassandra/Connections/Control/InternalMetadata.cs
new file mode 100644
index 000000000..985e68e7d
--- /dev/null
+++ b/src/Cassandra/Connections/Control/InternalMetadata.cs
@@ -0,0 +1,685 @@
+//
+// Copyright (C) DataStax Inc.
+//
+// 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.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+
+using Cassandra.Collections;
+using Cassandra.MetadataHelpers;
+using Cassandra.ProtocolEvents;
+using Cassandra.Requests;
+using Cassandra.Serialization;
+using Cassandra.SessionManagement;
+
+namespace Cassandra.Connections.Control
+{
+ internal class InternalMetadata : IInternalMetadata
+ {
+ private const string SelectSchemaVersionPeers = "SELECT schema_version FROM system.peers";
+ private const string SelectSchemaVersionLocal = "SELECT schema_version FROM system.local";
+
+ private static readonly Logger Logger = new Logger(typeof(ControlConnection));
+
+ private readonly Metadata _parent;
+ private readonly IProtocolEventDebouncer _protocolEventDebouncer;
+
+ private volatile TokenMap _tokenMap;
+ private volatile ConcurrentDictionary _keyspaces =
+ new ConcurrentDictionary();
+ private volatile ISchemaParser _schemaParser;
+ private volatile CopyOnWriteDictionary> _resolvedContactPoints =
+ new CopyOnWriteDictionary>();
+ private volatile bool _isDbaas = false;
+ private volatile string _clusterName;
+
+ ///
+ public Configuration Configuration { get; }
+
+ ///
+ public IControlConnection ControlConnection { get; }
+
+ public ISerializerManager SerializerManager { get; }
+
+ public ISchemaParser SchemaParser => _schemaParser;
+
+ public string Partitioner { get; set; }
+
+ public Hosts Hosts { get; }
+
+ public IReadOnlyDictionary> ResolvedContactPoints
+ => _resolvedContactPoints;
+
+ public IReadOnlyTokenMap TokenToReplicasMap => _tokenMap;
+
+ public bool IsDbaas => _isDbaas;
+
+ public string ClusterName => _clusterName;
+
+ public ProtocolVersion ProtocolVersion => SerializerManager.CurrentProtocolVersion;
+
+ internal InternalMetadata(
+ IInternalCluster cluster,
+ Metadata parent,
+ Configuration configuration,
+ ISerializerManager serializerManager,
+ IEnumerable parsedContactPoints)
+ {
+ _parent = parent;
+ SerializerManager = serializerManager;
+ Configuration = configuration;
+ Hosts = new Hosts();
+
+ _protocolEventDebouncer = new ProtocolEventDebouncer(
+ configuration.TimerFactory,
+ TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.RefreshSchemaDelayIncrement),
+ TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.MaxTotalRefreshSchemaDelay));
+
+ ControlConnection = configuration.ControlConnectionFactory.Create(
+ cluster,
+ _protocolEventDebouncer,
+ SerializerManager,
+ Configuration,
+ this,
+ parsedContactPoints);
+
+ Hosts.Down += OnHostDown;
+ Hosts.Up += OnHostUp;
+ }
+
+ internal InternalMetadata(
+ IInternalCluster cluster,
+ Metadata parent,
+ Configuration configuration,
+ ISerializerManager serializerManager,
+ IEnumerable contactPoints,
+ SchemaParser schemaParser)
+ : this(cluster, parent, configuration, serializerManager, contactPoints)
+ {
+ _schemaParser = schemaParser;
+ }
+
+ public Task InitAsync()
+ {
+ return ControlConnection.InitAsync();
+ }
+
+ public Task ShutdownAsync()
+ {
+ ControlConnection.Dispose();
+ return _protocolEventDebouncer.ShutdownAsync();
+ }
+
+ public void SetResolvedContactPoints(
+ IDictionary> resolvedContactPoints)
+ {
+ _resolvedContactPoints =
+ new CopyOnWriteDictionary>(resolvedContactPoints);
+ }
+
+ public void OnHostRemoved(Host h)
+ {
+ _parent.OnHostRemoved(h);
+ }
+
+ public void OnHostAdded(Host h)
+ {
+ _parent.OnHostAdded(h);
+ }
+
+ public Host GetHost(IPEndPoint address)
+ {
+ return Hosts.TryGet(address, out var host) ? host : null;
+ }
+
+ public Host AddHost(IPEndPoint address)
+ {
+ return Hosts.Add(address);
+ }
+
+ public Host AddHost(IPEndPoint address, IContactPoint contactPoint)
+ {
+ return Hosts.Add(address, contactPoint);
+ }
+
+ public void RemoveHost(IPEndPoint address)
+ {
+ Hosts.RemoveIfExists(address);
+ }
+
+ public void FireSchemaChangedEvent(SchemaChangedEventArgs.Kind what, string keyspace, string table, object sender = null)
+ {
+ _parent.FireSchemaChangedEvent(what, keyspace, table, sender);
+ }
+
+ public void OnHostDown(Host h)
+ {
+ _parent.OnHostDown(h);
+ }
+
+ public void OnHostUp(Host h)
+ {
+ _parent.OnHostUp(h);
+ }
+
+ ///
+ /// Returns all known hosts of this cluster.
+ ///
+ /// collection of all known hosts of this cluster.
+ public ICollection AllHosts()
+ {
+ return Hosts.ToCollection();
+ }
+
+ public IEnumerable AllReplicas()
+ {
+ return Hosts.AllEndPointsToCollection();
+ }
+
+ // for tests
+ public KeyValuePair[] KeyspacesSnapshot => _keyspaces.ToArray();
+
+ public async Task RebuildTokenMapAsync(bool retry, bool fetchKeyspaces)
+ {
+ IEnumerable ksList = null;
+ if (fetchKeyspaces)
+ {
+ InternalMetadata.Logger.Info("Retrieving keyspaces metadata");
+ ksList = await _schemaParser.GetKeyspacesAsync(retry).ConfigureAwait(false);
+ }
+
+ ConcurrentDictionary keyspaces;
+ if (ksList != null)
+ {
+ InternalMetadata.Logger.Info("Updating keyspaces metadata");
+ var ksMap = ksList.Select(ks => new KeyValuePair(ks.Name, ks));
+ keyspaces = new ConcurrentDictionary(ksMap);
+ }
+ else
+ {
+ keyspaces = _keyspaces;
+ }
+
+ InternalMetadata.Logger.Info("Rebuilding token map");
+ if (Partitioner == null)
+ {
+ throw new DriverInternalError("Partitioner can not be null");
+ }
+
+ var tokenMap = TokenMap.Build(Partitioner, Hosts.ToCollection(), keyspaces.Values);
+ _keyspaces = keyspaces;
+ _tokenMap = tokenMap;
+ }
+
+ ///
+ /// this method should be called by the event debouncer
+ ///
+ public bool RemoveKeyspaceFromTokenMap(string name)
+ {
+ InternalMetadata.Logger.Verbose("Removing keyspace metadata: " + name);
+ var dropped = _keyspaces.TryRemove(name, out _);
+ _tokenMap?.RemoveKeyspace(name);
+ return dropped;
+ }
+
+ public async Task UpdateTokenMapForKeyspace(string name)
+ {
+ var keyspaceMetadata = await _schemaParser.GetKeyspaceAsync(name).ConfigureAwait(false);
+
+ var dropped = false;
+ var updated = false;
+ if (_tokenMap == null)
+ {
+ await RebuildTokenMapAsync(false, false).ConfigureAwait(false);
+ }
+
+ if (keyspaceMetadata == null)
+ {
+ InternalMetadata.Logger.Verbose("Removing keyspace metadata: " + name);
+ dropped = _keyspaces.TryRemove(name, out _);
+ _tokenMap?.RemoveKeyspace(name);
+ }
+ else
+ {
+ InternalMetadata.Logger.Verbose("Updating keyspace metadata: " + name);
+ _keyspaces.AddOrUpdate(keyspaceMetadata.Name, keyspaceMetadata, (k, v) =>
+ {
+ updated = true;
+ return keyspaceMetadata;
+ });
+ InternalMetadata.Logger.Info("Rebuilding token map for keyspace {0}", keyspaceMetadata.Name);
+ if (Partitioner == null)
+ {
+ throw new DriverInternalError("Partitioner can not be null");
+ }
+
+ _tokenMap.UpdateKeyspace(keyspaceMetadata);
+ }
+
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ if (dropped)
+ {
+ FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Dropped, name, null, this);
+ }
+ else if (updated)
+ {
+ FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Updated, name, null, this);
+ }
+ else
+ {
+ FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Created, name, null, this);
+ }
+ }
+
+ return keyspaceMetadata;
+ }
+
+ ///
+ /// Get the replicas for a given partition key and keyspace
+ ///
+ public ICollection GetReplicas(string keyspaceName, byte[] partitionKey)
+ {
+ if (_tokenMap == null)
+ {
+ InternalMetadata.Logger.Warning("Metadata.GetReplicas was called but there was no token map.");
+ return new Host[0];
+ }
+
+ return _tokenMap.GetReplicas(keyspaceName, _tokenMap.Factory.Hash(partitionKey));
+ }
+
+ ///
+ /// Get the replicas for a given partition key
+ ///
+ public ICollection GetReplicas(byte[] partitionKey)
+ {
+ return GetReplicas(null, partitionKey);
+ }
+
+ ///
+ /// Returns metadata of specified keyspace.
+ ///
+ /// the name of the keyspace for which metadata should be
+ /// returned.
+ /// the metadata of the requested keyspace or null if
+ /// * keyspace is not a known keyspace.
+ public Task GetKeyspaceAsync(string keyspace)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ //Use local cache
+ _keyspaces.TryGetValue(keyspace, out var ksInfo);
+ return Task.FromResult(ksInfo);
+ }
+
+ return SchemaParser.GetKeyspaceAsync(keyspace);
+ }
+
+ ///
+ /// Returns a collection of all defined keyspaces names.
+ ///
+ /// a collection of all defined keyspaces names.
+ public Task> GetKeyspacesAsync()
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ //Use local cache
+ return Task.FromResult(_keyspaces.Keys);
+ }
+
+ return SchemaParser.GetKeyspacesNamesAsync();
+ }
+
+ ///
+ /// Returns names of all tables which are defined within specified keyspace.
+ ///
+ /// the name of the keyspace for which all tables metadata should be
+ /// returned.
+ /// an ICollection of the metadata for the tables defined in this
+ /// keyspace.
+ public Task> GetTablesAsync(string keyspace)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
+ ? Task.FromResult>(new string[0])
+ : ksMetadata.GetTablesNamesAsync();
+ }
+
+ return SchemaParser.GetTableNamesAsync(keyspace);
+ }
+
+ ///
+ /// Returns TableMetadata for specified table in specified keyspace.
+ ///
+ /// name of the keyspace within specified table is defined.
+ /// name of table for which metadata should be returned.
+ /// a TableMetadata for the specified table in the specified keyspace.
+ public Task GetTableAsync(string keyspace, string tableName)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
+ ? Task.FromResult(null)
+ : ksMetadata.GetTableMetadataAsync(tableName);
+ }
+
+ return SchemaParser.GetTableAsync(keyspace, tableName);
+ }
+
+ ///
+ /// Returns the view metadata for the provided view name in the keyspace.
+ ///
+ /// name of the keyspace within specified view is defined.
+ /// name of view.
+ /// a MaterializedViewMetadata for the view in the specified keyspace.
+ public Task GetMaterializedViewAsync(string keyspace, string name)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
+ ? null
+ : ksMetadata.GetMaterializedViewMetadataAsync(name);
+ }
+
+ return SchemaParser.GetViewAsync(keyspace, name);
+ }
+
+ ///
+ /// Gets the definition associated with a User Defined Type from Cassandra
+ ///
+ public Task GetUdtDefinitionAsync(string keyspace, string typeName)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
+ ? Task.FromResult(null)
+ : ksMetadata.GetUdtDefinitionAsync(typeName);
+ }
+
+ return SchemaParser.GetUdtDefinitionAsync(keyspace, typeName);
+ }
+
+ ///
+ /// Gets the definition associated with a User Defined Function from Cassandra
+ ///
+ /// The function metadata or null if not found.
+ public Task GetFunctionAsync(string keyspace, string name, string[] signature)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
+ ? Task.FromResult(null)
+ : ksMetadata.GetFunctionAsync(name, signature);
+ }
+
+ var signatureString = SchemaParser.ComputeFunctionSignatureString(signature);
+ return SchemaParser.GetFunctionAsync(keyspace, name, signatureString);
+ }
+
+ ///
+ /// Gets the definition associated with a aggregate from Cassandra
+ ///
+ /// The aggregate metadata or null if not found.
+ public Task GetAggregateAsync(string keyspace, string name, string[] signature)
+ {
+ if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
+ {
+ return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
+ ? Task.FromResult(null)
+ : ksMetadata.GetAggregateAsync(name, signature);
+ }
+
+ var signatureString = SchemaParser.ComputeFunctionSignatureString(signature);
+ return SchemaParser.GetAggregateAsync(keyspace, name, signatureString);
+ }
+
+ ///
+ /// Gets the query trace.
+ ///
+ /// The query trace that contains the id, which properties are going to be populated.
+ ///
+ public Task GetQueryTraceAsync(QueryTrace trace)
+ {
+ return _schemaParser.GetQueryTraceAsync(trace, Configuration.Timer);
+ }
+
+ ///
+ /// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
+ /// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
+ ///
+ public async Task RefreshSchemaAsync(string keyspace = null, string table = null)
+ {
+ if (keyspace == null)
+ {
+ await ControlConnection.ScheduleAllKeyspacesRefreshAsync(true).ConfigureAwait(false);
+ return true;
+ }
+
+ await ControlConnection.ScheduleKeyspaceRefreshAsync(keyspace, true).ConfigureAwait(false);
+ _keyspaces.TryGetValue(keyspace, out var ks);
+ if (ks == null)
+ {
+ return false;
+ }
+
+ if (table != null)
+ {
+ ks.ClearTableMetadata(table);
+ }
+ return true;
+ }
+
+ ///
+ /// this method should be called by the event debouncer
+ ///
+ public bool RemoveKeyspace(string name)
+ {
+ var existed = RemoveKeyspaceFromTokenMap(name);
+ if (!existed)
+ {
+ return false;
+ }
+
+ FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Dropped, name, null, this);
+ return true;
+ }
+
+ ///
+ /// this method should be called by the event debouncer
+ ///
+ public Task RefreshSingleKeyspaceAsync(string name)
+ {
+ return UpdateTokenMapForKeyspace(name);
+ }
+
+ public void ClearTable(string keyspaceName, string tableName)
+ {
+ if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
+ {
+ ksMetadata.ClearTableMetadata(tableName);
+ }
+ }
+
+ public void ClearView(string keyspaceName, string name)
+ {
+ if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
+ {
+ ksMetadata.ClearViewMetadata(name);
+ }
+ }
+
+ public void ClearFunction(string keyspaceName, string functionName, string[] signature)
+ {
+ if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
+ {
+ ksMetadata.ClearFunction(functionName, signature);
+ }
+ }
+
+ public void ClearAggregate(string keyspaceName, string aggregateName, string[] signature)
+ {
+ if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
+ {
+ ksMetadata.ClearAggregate(aggregateName, signature);
+ }
+ }
+
+ ///
+ /// Initiates a schema agreement check.
+ ///
+ /// Schema changes need to be propagated to all nodes in the cluster.
+ /// Once they have settled on a common version, we say that they are in agreement.
+ ///
+ /// This method does not perform retries so
+ /// does not apply.
+ ///
+ /// True if schema agreement was successful and false if it was not successful.
+ public async Task CheckSchemaAgreementAsync()
+ {
+ if (Hosts.Count == 1)
+ {
+ // If there is just one node, the schema is up to date in all nodes :)
+ return true;
+ }
+
+ try
+ {
+ var queries = new[]
+ {
+ ControlConnection.QueryAsync(SelectSchemaVersionLocal),
+ ControlConnection.QueryAsync(SelectSchemaVersionPeers)
+ };
+
+ await Task.WhenAll(queries).ConfigureAwait(false);
+
+ return CheckSchemaVersionResults(queries[0].Result, queries[1].Result);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error("Error while checking schema agreement.", ex);
+ }
+
+ return false;
+ }
+
+ ///
+ /// Checks if there is only one schema version between the provided query results.
+ ///
+ ///
+ /// Results obtained from a query to system.local table.
+ /// Must contain the schema_version column.
+ ///
+ ///
+ /// Results obtained from a query to system.peers table.
+ /// Must contain the schema_version column.
+ ///
+ /// True if there is a schema agreement (only 1 schema version). False otherwise.
+ private static bool CheckSchemaVersionResults(
+ IEnumerable localVersionQuery, IEnumerable peerVersionsQuery)
+ {
+ return new HashSet(
+ peerVersionsQuery
+ .Concat(localVersionQuery)
+ .Select(r => r.GetValue("schema_version"))).Count == 1;
+ }
+
+ ///
+ /// Waits until that the schema version in all nodes is the same or the waiting time passed.
+ /// This method blocks the calling thread.
+ ///
+ public async Task WaitForSchemaAgreementAsync(IConnection connection)
+ {
+ if (Hosts.Count == 1)
+ {
+ //If there is just one node, the schema is up to date in all nodes :)
+ return true;
+ }
+ var start = DateTime.Now;
+ var waitSeconds = Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds;
+ InternalMetadata.Logger.Info("Waiting for schema agreement");
+ try
+ {
+ var totalVersions = 0;
+ while (DateTime.Now.Subtract(start).TotalSeconds < waitSeconds)
+ {
+ var serializer = SerializerManager.GetCurrentSerializer();
+ var schemaVersionLocalQuery =
+ new QueryRequest(
+ serializer,
+ InternalMetadata.SelectSchemaVersionLocal,
+ QueryProtocolOptions.Default,
+ false,
+ null);
+ var schemaVersionPeersQuery =
+ new QueryRequest(
+ serializer,
+ InternalMetadata.SelectSchemaVersionPeers,
+ QueryProtocolOptions.Default,
+ false,
+ null);
+ var queries = new[] { connection.Send(schemaVersionLocalQuery), connection.Send(schemaVersionPeersQuery) };
+ // ReSharper disable once CoVariantArrayConversion
+ await Task.WhenAll(queries);
+
+ if (InternalMetadata.CheckSchemaVersionResults(
+ Configuration.MetadataRequestHandler.GetRowSet(await queries[0].ConfigureAwait(false)),
+ Configuration.MetadataRequestHandler.GetRowSet(await queries[1].ConfigureAwait(false))))
+ {
+ return true;
+ }
+
+ await Task.Delay(500).ConfigureAwait(false);
+ }
+ InternalMetadata.Logger.Info($"Waited for schema agreement, still {totalVersions} schema versions in the cluster.");
+ }
+ catch (Exception ex)
+ {
+ //Exceptions are not fatal
+ InternalMetadata.Logger.Error("There was an exception while trying to retrieve schema versions", ex);
+ }
+
+ return false;
+ }
+
+ ///
+ /// Sets the Cassandra version in order to identify how to parse the metadata information
+ ///
+ ///
+ public void SetCassandraVersion(Version version)
+ {
+ _schemaParser = Configuration.SchemaParserFactory.Create(version, this, GetUdtDefinitionAsync, _schemaParser);
+ }
+
+ public void SetProductTypeAsDbaas()
+ {
+ _isDbaas = true;
+ }
+
+ public void SetClusterName(string clusterName)
+ {
+ _clusterName = clusterName;
+ }
+
+ public IEnumerable UpdateResolvedContactPoint(IContactPoint contactPoint, IEnumerable endpoints)
+ {
+ return _resolvedContactPoints.AddOrUpdate(contactPoint, _ => endpoints, (_, __) => endpoints);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs b/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs
index 47b01644d..81b542a33 100644
--- a/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs
+++ b/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs
@@ -22,11 +22,11 @@ internal class ProtocolVersionNegotiator : IProtocolVersionNegotiator
{
public async Task NegotiateVersionAsync(
Configuration config,
- Metadata metadata,
+ IInternalMetadata internalMetadata,
IConnection connection,
ISerializerManager serializer)
{
- var commonVersion = serializer.CurrentProtocolVersion.GetHighestCommon(config, metadata.Hosts);
+ var commonVersion = serializer.CurrentProtocolVersion.GetHighestCommon(config, internalMetadata.Hosts);
if (commonVersion != serializer.CurrentProtocolVersion)
{
// Current connection will be closed and reopened
diff --git a/src/Cassandra/Connections/Control/SupportedOptionsInitializer.cs b/src/Cassandra/Connections/Control/SupportedOptionsInitializer.cs
index e857a93d7..019f6fa7c 100644
--- a/src/Cassandra/Connections/Control/SupportedOptionsInitializer.cs
+++ b/src/Cassandra/Connections/Control/SupportedOptionsInitializer.cs
@@ -26,11 +26,11 @@ internal class SupportedOptionsInitializer : ISupportedOptionsInitializer
private const string SupportedProductTypeKey = "PRODUCT_TYPE";
private const string SupportedDbaas = "DATASTAX_APOLLO";
- private readonly Metadata _metadata;
+ private readonly IInternalMetadata _internalMetadata;
- public SupportedOptionsInitializer(Metadata metadata)
+ public SupportedOptionsInitializer(IInternalMetadata internalMetadata)
{
- _metadata = metadata;
+ _internalMetadata = internalMetadata;
}
public async Task ApplySupportedOptionsAsync(IConnection connection)
@@ -65,7 +65,7 @@ private void ApplyProductTypeOption(IDictionary options)
if (string.Compare(productTypeOptions[0], SupportedOptionsInitializer.SupportedDbaas, StringComparison.OrdinalIgnoreCase) == 0)
{
- _metadata.SetProductTypeAsDbaas();
+ _internalMetadata.SetProductTypeAsDbaas();
}
}
}
diff --git a/src/Cassandra/Connections/Control/SupportedOptionsInitializerFactory.cs b/src/Cassandra/Connections/Control/SupportedOptionsInitializerFactory.cs
index ba080b28c..56b479bf5 100644
--- a/src/Cassandra/Connections/Control/SupportedOptionsInitializerFactory.cs
+++ b/src/Cassandra/Connections/Control/SupportedOptionsInitializerFactory.cs
@@ -17,9 +17,9 @@ namespace Cassandra.Connections.Control
{
internal class SupportedOptionsInitializerFactory : ISupportedOptionsInitializerFactory
{
- public ISupportedOptionsInitializer Create(Metadata metadata)
+ public ISupportedOptionsInitializer Create(IInternalMetadata internalMetadata)
{
- return new SupportedOptionsInitializer(metadata);
+ return new SupportedOptionsInitializer(internalMetadata);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/TopologyRefresher.cs b/src/Cassandra/Connections/Control/TopologyRefresher.cs
index 49f386e36..f151a28b5 100644
--- a/src/Cassandra/Connections/Control/TopologyRefresher.cs
+++ b/src/Cassandra/Connections/Control/TopologyRefresher.cs
@@ -1,12 +1,12 @@
-//
+//
// Copyright (C) DataStax Inc.
-//
+//
// 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.
@@ -18,9 +18,9 @@
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+
using Cassandra.Responses;
using Cassandra.Serialization;
-using Cassandra.Tasks;
namespace Cassandra.Connections.Control
{
@@ -34,16 +34,16 @@ internal class TopologyRefresher : ITopologyRefresher
private static readonly IPAddress BindAllAddress = new IPAddress(new byte[4]);
private readonly Configuration _config;
- private readonly Metadata _metadata;
+ private readonly IInternalMetadata _internalMetadata;
///
/// Once this is set to false, it will never be set to true again.
///
private volatile bool _isPeersV2 = true;
- public TopologyRefresher(Metadata metadata, Configuration config)
+ public TopologyRefresher(IInternalMetadata internalMetadata, Configuration config)
{
- _metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
+ _internalMetadata = internalMetadata ?? throw new ArgumentNullException(nameof(internalMetadata));
_config = config ?? throw new ArgumentNullException(nameof(config));
}
@@ -58,14 +58,14 @@ public async Task RefreshNodeListAsync(
var localTask = SendSystemLocalRequestAsync(connection, serializer);
var peersTask = SendSystemPeersRequestAsync(localIsPeersV2, connection, serializer);
-
+
await Task.WhenAll(localTask, peersTask).ConfigureAwait(false);
var peersResponse = peersTask.Result;
localIsPeersV2 = peersResponse.IsPeersV2;
var rsPeers = _config.MetadataRequestHandler.GetRowSet(peersResponse.Response);
-
+
var localRow = _config.MetadataRequestHandler.GetRowSet(localTask.Result).FirstOrDefault();
if (localRow == null)
{
@@ -73,13 +73,13 @@ public async Task RefreshNodeListAsync(
throw new DriverInternalError("Local host metadata could not be retrieved");
}
- _metadata.Partitioner = localRow.GetValue("partitioner");
+ _internalMetadata.Partitioner = localRow.GetValue("partitioner");
var host = GetAndUpdateLocalHost(currentEndPoint, localRow);
UpdatePeersInfo(localIsPeersV2, rsPeers, host);
ControlConnection.Logger.Info("Node list retrieved successfully");
return host;
}
-
+
private Task SendSystemLocalRequestAsync(IConnection connection, ISerializer serializer)
{
return _config.MetadataRequestHandler.SendMetadataRequestAsync(
@@ -89,9 +89,9 @@ private Task SendSystemLocalRequestAsync(IConnection connection, ISeri
private Task SendSystemPeersRequestAsync(bool isPeersV2, IConnection connection, ISerializer serializer)
{
var peersTask = _config.MetadataRequestHandler.SendMetadataRequestAsync(
- connection,
- serializer,
- isPeersV2 ? TopologyRefresher.SelectPeersV2 : TopologyRefresher.SelectPeers,
+ connection,
+ serializer,
+ isPeersV2 ? TopologyRefresher.SelectPeersV2 : TopologyRefresher.SelectPeers,
QueryProtocolOptions.Default);
return GetPeersResponseAsync(isPeersV2, peersTask, connection, serializer);
@@ -134,8 +134,8 @@ private async Task GetPeersResponseAsync(
///
private Host GetAndUpdateLocalHost(IConnectionEndPoint endPoint, IRow row)
{
- var hostIpEndPoint =
- endPoint.GetHostIpEndPoint()
+ var hostIpEndPoint =
+ endPoint.GetHostIpEndPoint()
?? GetRpcEndPoint(false, row, _config.AddressTranslator, _config.ProtocolOptions.Port);
if (hostIpEndPoint == null)
@@ -143,14 +143,14 @@ private Host GetAndUpdateLocalHost(IConnectionEndPoint endPoint, IRow row)
throw new DriverInternalError("Could not parse the node's ip address from system tables.");
}
- var host = _metadata.GetHost(hostIpEndPoint) ?? _metadata.AddHost(hostIpEndPoint, endPoint.ContactPoint);
+ var host = _internalMetadata.GetHost(hostIpEndPoint) ?? _internalMetadata.AddHost(hostIpEndPoint, endPoint.ContactPoint);
// Update cluster name, DC and rack for the one node we are connected to
var clusterName = row.GetValue("cluster_name");
if (clusterName != null)
{
- _metadata.ClusterName = clusterName;
+ _internalMetadata.SetClusterName(clusterName);
}
host.SetInfo(row);
@@ -173,20 +173,20 @@ private void UpdatePeersInfo(bool isPeersV2, IEnumerable peersRs, Host cur
}
foundPeers.Add(address);
- var host = _metadata.GetHost(address) ?? _metadata.AddHost(address);
+ var host = _internalMetadata.GetHost(address) ?? _internalMetadata.AddHost(address);
host.SetInfo(row);
}
// Removes all those that seems to have been removed (since we lost the control connection or not valid contact point)
- foreach (var address in _metadata.AllReplicas())
+ foreach (var address in _internalMetadata.AllReplicas())
{
if (!address.Equals(currentHost.Address) && !foundPeers.Contains(address))
{
- _metadata.RemoveHost(address);
+ _internalMetadata.RemoveHost(address);
}
}
}
-
+
///
/// Parses address from system table query response and translates it using the provided .
///
@@ -225,17 +225,17 @@ internal IPEndPoint GetRpcEndPoint(bool isPeersV2, IRow row, IAddressTranslator
return translator.Translate(new IPEndPoint(address, rpcPort));
}
-
+
private IPAddress GetRpcAddressFromPeersV2(IRow row)
{
return row.GetValue("native_address");
}
-
+
private IPAddress GetRpcAddressFromLocalPeersV1(IRow row)
{
return row.GetValue("rpc_address");
}
-
+
private int? GetRpcPortFromPeersV2(IRow row)
{
return row.GetValue("native_port");
@@ -243,9 +243,9 @@ private IPAddress GetRpcAddressFromLocalPeersV1(IRow row)
private class PeersResponse
{
- public bool IsPeersV2 { get; set; }
+ public bool IsPeersV2 { get; set; }
- public Response Response { get; set; }
+ public Response Response { get; set; }
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Connections/Control/TopologyRefresherFactory.cs b/src/Cassandra/Connections/Control/TopologyRefresherFactory.cs
index d6c969fa1..22331e3f2 100644
--- a/src/Cassandra/Connections/Control/TopologyRefresherFactory.cs
+++ b/src/Cassandra/Connections/Control/TopologyRefresherFactory.cs
@@ -17,9 +17,9 @@ namespace Cassandra.Connections.Control
{
internal class TopologyRefresherFactory : ITopologyRefresherFactory
{
- public ITopologyRefresher Create(Metadata metadata, Configuration config)
+ public ITopologyRefresher Create(IInternalMetadata internalMetadata, Configuration config)
{
- return new TopologyRefresher(metadata, config);
+ return new TopologyRefresher(internalMetadata, config);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/DataStax/Insights/IInsightsSupportVerifier.cs b/src/Cassandra/DataStax/Insights/IInsightsSupportVerifier.cs
index 7abdc449e..01bba9a81 100644
--- a/src/Cassandra/DataStax/Insights/IInsightsSupportVerifier.cs
+++ b/src/Cassandra/DataStax/Insights/IInsightsSupportVerifier.cs
@@ -15,12 +15,13 @@
//
using System;
+using Cassandra.Connections.Control;
namespace Cassandra.DataStax.Insights
{
internal interface IInsightsSupportVerifier
{
- bool SupportsInsights(Metadata metadata);
+ bool SupportsInsights(IInternalMetadata internalMetadata);
bool DseVersionSupportsInsights(Version dseVersion);
}
diff --git a/src/Cassandra/DataStax/Insights/InsightsSupportVerifier.cs b/src/Cassandra/DataStax/Insights/InsightsSupportVerifier.cs
index 51bbc1489..c66034843 100644
--- a/src/Cassandra/DataStax/Insights/InsightsSupportVerifier.cs
+++ b/src/Cassandra/DataStax/Insights/InsightsSupportVerifier.cs
@@ -16,6 +16,7 @@
using System;
using System.Linq;
+using Cassandra.Connections.Control;
namespace Cassandra.DataStax.Insights
{
@@ -25,9 +26,9 @@ internal class InsightsSupportVerifier : IInsightsSupportVerifier
private static readonly Version MinDse51Version = new Version(5, 1, 13);
private static readonly Version Dse600Version = new Version(6, 0, 0);
- public bool SupportsInsights(Metadata metadata)
+ public bool SupportsInsights(IInternalMetadata internalMetadata)
{
- var allHosts = metadata.AllHosts();
+ var allHosts = internalMetadata.AllHosts();
return allHosts.Count != 0 && allHosts.All(h => DseVersionSupportsInsights(h.DseVersion));
}
diff --git a/src/Cassandra/ICluster.cs b/src/Cassandra/ICluster.cs
index 2067687eb..392f66998 100644
--- a/src/Cassandra/ICluster.cs
+++ b/src/Cassandra/ICluster.cs
@@ -44,26 +44,16 @@ namespace Cassandra
public interface ICluster : IDisposable
{
///
- /// This property may block while waiting for the creation of a connection if none has been established yet.
- /// Use instead if your application uses the Task Parallel Library (e.g. async/await).
+ ///
+ /// Gets an utility class that allows you to fetch metadata about the connected cluster.
+ /// This includes the known nodes (with their status as seen by the driver) as well as the schema definitions.
///
- /// Gets read-only metadata on the connected cluster.
- /// This includes the
- /// know nodes (with their status as seen by the driver) as well as the schema definitions.
+ ///
+ /// It also allows you to subscribe to certain events (e.g. ).
///
///
Metadata Metadata { get; }
- ///
- /// Gets read-only metadata on the connected cluster.
- /// This includes the
- /// know nodes (with their status as seen by the driver) as well as the schema definitions.
- ///
- /// This method may trigger the creation of a connection if none has been established yet.
- ///
- ///
- Task GetMetadataAsync();
-
///
/// Cluster client configuration
///
diff --git a/src/Cassandra/KeyspaceMetadata.cs b/src/Cassandra/KeyspaceMetadata.cs
index 13f6ac536..96b8f1117 100644
--- a/src/Cassandra/KeyspaceMetadata.cs
+++ b/src/Cassandra/KeyspaceMetadata.cs
@@ -127,8 +127,14 @@ public TableMetadata GetTableMetadata(string tableName)
return TaskHelper.WaitToComplete(
GetTableMetadataAsync(tableName), _parent.Configuration.DefaultRequestOptions.GetQueryAbortTimeout(2));
}
-
- internal async Task GetTableMetadataAsync(string tableName)
+
+ ///
+ /// Returns metadata of specified table in this keyspace.
+ ///
+ /// the name of table to retrieve
+ /// the metadata for table tableName in this keyspace if it
+ /// exists, null otherwise.
+ public async Task GetTableMetadataAsync(string tableName)
{
if (_tables.TryGetValue(tableName, out var tableMetadata))
{
@@ -159,8 +165,14 @@ public MaterializedViewMetadata GetMaterializedViewMetadata(string viewName)
return TaskHelper.WaitToComplete(
GetMaterializedViewMetadataAsync(viewName), _parent.Configuration.DefaultRequestOptions.GetQueryAbortTimeout(2));
}
-
- private async Task GetMaterializedViewMetadataAsync(string viewName)
+
+ ///
+ /// Returns metadata of specified view in this keyspace.
+ ///
+ /// the name of view to retrieve
+ /// the metadata for view viewName in this keyspace if it
+ /// exists, null otherwise.
+ public async Task GetMaterializedViewMetadataAsync(string viewName)
{
if (_views.TryGetValue(viewName, out var v))
{
@@ -218,11 +230,20 @@ internal void ClearAggregate(string name, string[] signature)
/// keyspace.
public IEnumerable GetTablesMetadata()
{
- var tableNames = GetTablesNames();
+ return TaskHelper.WaitToComplete(GetTablesMetadataAsync());
+ }
+
+ ///
+ /// Returns metadata of all tables defined in this keyspace.
+ ///
+ /// an IEnumerable of TableMetadata for the tables defined in this
+ /// keyspace.
+ public async Task> GetTablesMetadataAsync()
+ {
+ var tableNames = await GetTablesNamesAsync().ConfigureAwait(false);
return tableNames.Select(GetTableMetadata);
}
-
-
+
///
/// Returns names of all tables defined in this keyspace.
///
@@ -231,7 +252,18 @@ public IEnumerable GetTablesMetadata()
/// keyspace tables names.
public ICollection GetTablesNames()
{
- return TaskHelper.WaitToComplete(_parent.SchemaParser.GetTableNamesAsync(Name));
+ return TaskHelper.WaitToComplete(GetTablesNamesAsync());
+ }
+
+ ///
+ /// Returns names of all tables defined in this keyspace.
+ ///
+ ///
+ /// a collection of all, defined in this
+ /// keyspace tables names.
+ public Task> GetTablesNamesAsync()
+ {
+ return _parent.SchemaParser.GetTableNamesAsync(Name);
}
///
@@ -305,8 +337,12 @@ public FunctionMetadata GetFunction(string functionName, string[] signature)
return TaskHelper.WaitToComplete(
GetFunctionAsync(functionName, signature), _parent.Configuration.DefaultRequestOptions.QueryAbortTimeout);
}
-
- private async Task GetFunctionAsync(string functionName, string[] signature)
+
+ ///
+ /// Gets a CQL function by name and signature
+ ///
+ /// The function metadata or null if not found.
+ public async Task GetFunctionAsync(string functionName, string[] signature)
{
if (signature == null)
{
@@ -339,8 +375,12 @@ public AggregateMetadata GetAggregate(string aggregateName, string[] signature)
return TaskHelper.WaitToComplete(
GetAggregateAsync(aggregateName, signature), _parent.Configuration.DefaultRequestOptions.QueryAbortTimeout);
}
-
- private async Task GetAggregateAsync(string aggregateName, string[] signature)
+
+ ///
+ /// Gets a CQL aggregate by name and signature
+ ///
+ /// The aggregate metadata or null if not found.
+ public async Task GetAggregateAsync(string aggregateName, string[] signature)
{
if (signature == null)
{
diff --git a/src/Cassandra/Metadata.cs b/src/Cassandra/Metadata.cs
index 366b132b0..399215ff5 100644
--- a/src/Cassandra/Metadata.cs
+++ b/src/Cassandra/Metadata.cs
@@ -15,18 +15,12 @@
//
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using Cassandra.Collections;
-using Cassandra.Connections;
+
using Cassandra.Connections.Control;
-using Cassandra.MetadataHelpers;
-using Cassandra.ProtocolEvents;
-using Cassandra.Requests;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
using Cassandra.Tasks;
@@ -39,33 +33,23 @@ namespace Cassandra
///
public class Metadata
{
- private const string SelectSchemaVersionPeers = "SELECT schema_version FROM system.peers";
- private const string SelectSchemaVersionLocal = "SELECT schema_version FROM system.local";
- private static readonly Logger Logger = new Logger(typeof(ControlConnection));
- private readonly ISerializerManager _serializerManager;
- private volatile TokenMap _tokenMap;
- private volatile ConcurrentDictionary _keyspaces = new ConcurrentDictionary();
- private volatile ISchemaParser _schemaParser;
- private readonly IProtocolEventDebouncer _protocolEventDebouncer;
+ private const int Disposed = 10;
+ private const int Initialized = 5;
+ private const int Initializing = 1;
+ private const int NotInitialized = 0;
+
+ private readonly InternalMetadata _internalMetadata;
private readonly int _queryAbortTimeout;
- private volatile CopyOnWriteDictionary> _resolvedContactPoints =
- new CopyOnWriteDictionary>();
+ private volatile Task _initTask;
+
+ private long _state = Metadata.NotInitialized;
+
+ internal InternalMetadata InternalMetadata => _internalMetadata;
public event HostsEventHandler HostsEvent;
public event SchemaChangedEventHandler SchemaChangedEvent;
- ///
- /// Returns the name of currently connected cluster.
- ///
- /// the Cassandra name of currently connected cluster.
- public string ClusterName { get; internal set; }
-
- ///
- /// Determines whether the cluster is provided as a service (DataStax Astra).
- ///
- public bool IsDbaas { get; private set; } = false;
-
///
/// Event that gets triggered when a new host is added to the cluster
///
@@ -76,88 +60,79 @@ public class Metadata
///
public event Action HostRemoved;
- ///
- /// Gets the configuration associated with this instance.
- ///
- internal Configuration Configuration { get; private set; }
-
- ///
- /// Control connection to be used to execute the queries to retrieve the metadata
- ///
- internal IControlConnection ControlConnection { get; }
-
- ///
- /// Gets the Cassandra native binary protocol version
- ///
- public ProtocolVersion ProtocolVersion => _serializerManager.GetCurrentSerializer().ProtocolVersion;
-
- internal ISerializerManager SerializerManager => _serializerManager;
-
- internal ISchemaParser SchemaParser { get { return _schemaParser; } }
-
- internal string Partitioner { get; set; }
-
- internal Hosts Hosts { get; private set; }
-
- internal IReadOnlyDictionary> ResolvedContactPoints => _resolvedContactPoints;
-
- internal IReadOnlyTokenMap TokenToReplicasMap => _tokenMap;
-
internal Metadata(
- IInternalCluster cluster,
- Configuration configuration,
- ISerializerManager serializerManager,
+ IInternalCluster cluster,
+ Configuration configuration,
+ ISerializerManager serializerManager,
IEnumerable parsedContactPoints)
{
- _serializerManager = serializerManager;
_queryAbortTimeout = configuration.DefaultRequestOptions.QueryAbortTimeout;
- Configuration = configuration;
- Hosts = new Hosts();
-
- _protocolEventDebouncer = new ProtocolEventDebouncer(
- configuration.TimerFactory,
- TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.RefreshSchemaDelayIncrement),
- TimeSpan.FromMilliseconds(configuration.MetadataSyncOptions.MaxTotalRefreshSchemaDelay));
-
- ControlConnection = configuration.ControlConnectionFactory.Create(
- cluster,
- _protocolEventDebouncer,
- _serializerManager,
- Configuration,
- this,
- parsedContactPoints);
-
- Hosts.Down += OnHostDown;
- Hosts.Up += OnHostUp;
+ _internalMetadata = new InternalMetadata(cluster, this, configuration, serializerManager, parsedContactPoints);
}
internal Metadata(
IInternalCluster cluster,
- Configuration configuration,
- ISerializerManager serializerManager,
- IEnumerable contactPoints,
- SchemaParser schemaParser)
- : this(cluster, configuration, serializerManager, contactPoints)
+ Configuration configuration,
+ ISerializerManager serializerManager,
+ IEnumerable parsedContactPoints,
+ SchemaParser schemaParser)
{
- _schemaParser = schemaParser;
+ _queryAbortTimeout = configuration.DefaultRequestOptions.QueryAbortTimeout;
+ _internalMetadata = new InternalMetadata(
+ cluster, this, configuration, serializerManager, parsedContactPoints, schemaParser);
}
- internal Task InitializeAsync()
+ internal Task TryInitializeAsync()
{
- return ControlConnection.InitAsync();
+ var currentState = Interlocked.Read(ref _state);
+ if (currentState == Metadata.Initialized)
+ {
+ //It was already initialized
+ return TaskHelper.Completed;
+ }
+
+ return InitializeAsync(currentState);
+ }
+
+ private Task InitializeAsync(long currentState)
+ {
+ if (currentState == Metadata.Disposed)
+ {
+ throw new ObjectDisposedException("This metadata object has been disposed.");
+ }
+
+ if (Interlocked.CompareExchange(ref _state, Metadata.Initializing, Metadata.NotInitialized)
+ == Metadata.NotInitialized)
+ {
+ _initTask = Task.Run(InitializeInternalAsync);
+ }
+
+ return _initTask;
}
- internal Task ShutdownAsync()
+ private async Task InitializeInternalAsync()
{
- ControlConnection.Dispose();
- return _protocolEventDebouncer.ShutdownAsync();
+ await _internalMetadata.InitAsync().ConfigureAwait(false);
+ var previousState = Interlocked.CompareExchange(ref _state, Metadata.Initialized, Metadata.Initializing);
+ if (previousState == Metadata.Disposed)
+ {
+ await _internalMetadata.ShutdownAsync().ConfigureAwait(false);
+ throw new ObjectDisposedException("Metadata instance was disposed before initialization finished.");
+ }
}
- internal void SetResolvedContactPoints(IDictionary> resolvedContactPoints)
+ internal async Task ShutdownAsync()
{
- _resolvedContactPoints = new CopyOnWriteDictionary>(resolvedContactPoints);
+ var previousState = Interlocked.Exchange(ref _state, Metadata.Disposed);
+
+ if (previousState != Metadata.Initialized)
+ {
+ return;
+ }
+
+ await _internalMetadata.ShutdownAsync().ConfigureAwait(false);
}
-
+
internal void OnHostRemoved(Host h)
{
HostRemoved?.Invoke(h);
@@ -168,26 +143,27 @@ internal void OnHostAdded(Host h)
HostAdded?.Invoke(h);
}
- public Host GetHost(IPEndPoint address)
+ public async Task GetClusterDescriptionAsync()
{
- if (Hosts.TryGet(address, out var host))
- return host;
- return null;
+ await TryInitializeAsync().ConfigureAwait(false);
+ return new ClusterDescription(
+ _internalMetadata.ClusterName, _internalMetadata.IsDbaas, _internalMetadata.ProtocolVersion);
}
- internal Host AddHost(IPEndPoint address)
+ public ClusterDescription GetClusterDescription()
{
- return Hosts.Add(address);
+ return TaskHelper.WaitToComplete(GetClusterDescriptionAsync());
}
- internal Host AddHost(IPEndPoint address, IContactPoint contactPoint)
+ public Host GetHost(IPEndPoint address)
{
- return Hosts.Add(address, contactPoint);
+ return TaskHelper.WaitToComplete(GetHostAsync(address), _queryAbortTimeout);
}
- internal void RemoveHost(IPEndPoint address)
+ public async Task GetHostAsync(IPEndPoint address)
{
- Hosts.RemoveIfExists(address);
+ await TryInitializeAsync().ConfigureAwait(false);
+ return _internalMetadata.GetHost(address);
}
internal void FireSchemaChangedEvent(SchemaChangedEventArgs.Kind what, string keyspace, string table, object sender = null)
@@ -195,12 +171,12 @@ internal void FireSchemaChangedEvent(SchemaChangedEventArgs.Kind what, string ke
SchemaChangedEvent?.Invoke(sender ?? this, new SchemaChangedEventArgs { Keyspace = keyspace, What = what, Table = table });
}
- private void OnHostDown(Host h)
+ internal void OnHostDown(Host h)
{
HostsEvent?.Invoke(this, new HostsEventArgs { Address = h.Address, What = HostsEventArgs.Kind.Down });
}
- private void OnHostUp(Host h)
+ internal void OnHostUp(Host h)
{
HostsEvent?.Invoke(h, new HostsEventArgs { Address = h.Address, What = HostsEventArgs.Kind.Up });
}
@@ -211,129 +187,61 @@ private void OnHostUp(Host h)
/// collection of all known hosts of this cluster.
public ICollection AllHosts()
{
- return Hosts.ToCollection();
+ return TaskHelper.WaitToComplete(AllHostsAsync(), _queryAbortTimeout);
}
- public IEnumerable AllReplicas()
+ ///
+ /// Returns all known hosts of this cluster.
+ ///
+ /// collection of all known hosts of this cluster.
+ public async Task> AllHostsAsync()
{
- return Hosts.AllEndPointsToCollection();
+ await TryInitializeAsync().ConfigureAwait(false);
+ return _internalMetadata.AllHosts();
}
- // for tests
- internal KeyValuePair[] KeyspacesSnapshot => _keyspaces.ToArray();
-
- internal async Task RebuildTokenMapAsync(bool retry, bool fetchKeyspaces)
+ public IEnumerable AllReplicas()
{
- IEnumerable ksList = null;
- if (fetchKeyspaces)
- {
- Metadata.Logger.Info("Retrieving keyspaces metadata");
- ksList = await _schemaParser.GetKeyspacesAsync(retry).ConfigureAwait(false);
- }
-
- ConcurrentDictionary keyspaces;
- if (ksList != null)
- {
- Metadata.Logger.Info("Updating keyspaces metadata");
- var ksMap = ksList.Select(ks => new KeyValuePair(ks.Name, ks));
- keyspaces = new ConcurrentDictionary(ksMap);
- }
- else
- {
- keyspaces = _keyspaces;
- }
-
- Metadata.Logger.Info("Rebuilding token map");
- if (Partitioner == null)
- {
- throw new DriverInternalError("Partitioner can not be null");
- }
+ return TaskHelper.WaitToComplete(AllReplicasAsync(), _queryAbortTimeout);
+ }
- var tokenMap = TokenMap.Build(Partitioner, Hosts.ToCollection(), keyspaces.Values);
- _keyspaces = keyspaces;
- _tokenMap = tokenMap;
+ public async Task> AllReplicasAsync()
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return _internalMetadata.AllReplicas();
}
///
- /// this method should be called by the event debouncer
+ /// Get the replicas for a given partition key and keyspace
///
- internal bool RemoveKeyspaceFromTokenMap(string name)
+ public ICollection GetReplicas(string keyspaceName, byte[] partitionKey)
{
- Metadata.Logger.Verbose("Removing keyspace metadata: " + name);
- var dropped = _keyspaces.TryRemove(name, out _);
- _tokenMap?.RemoveKeyspace(name);
- return dropped;
+ return TaskHelper.WaitToComplete(GetReplicasAsync(keyspaceName, partitionKey));
}
- internal async Task UpdateTokenMapForKeyspace(string name)
+ ///
+ /// Get the replicas for a given partition key
+ ///
+ public ICollection GetReplicas(byte[] partitionKey)
{
- var keyspaceMetadata = await _schemaParser.GetKeyspaceAsync(name).ConfigureAwait(false);
-
- var dropped = false;
- var updated = false;
- if (_tokenMap == null)
- {
- await RebuildTokenMapAsync(false, false).ConfigureAwait(false);
- }
-
- if (keyspaceMetadata == null)
- {
- Metadata.Logger.Verbose("Removing keyspace metadata: " + name);
- dropped = _keyspaces.TryRemove(name, out _);
- _tokenMap?.RemoveKeyspace(name);
- }
- else
- {
- Metadata.Logger.Verbose("Updating keyspace metadata: " + name);
- _keyspaces.AddOrUpdate(keyspaceMetadata.Name, keyspaceMetadata, (k, v) =>
- {
- updated = true;
- return keyspaceMetadata;
- });
- Metadata.Logger.Info("Rebuilding token map for keyspace {0}", keyspaceMetadata.Name);
- if (Partitioner == null)
- {
- throw new DriverInternalError("Partitioner can not be null");
- }
-
- _tokenMap.UpdateKeyspace(keyspaceMetadata);
- }
-
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- if (dropped)
- {
- FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Dropped, name, null, this);
- }
- else if (updated)
- {
- FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Updated, name, null, this);
- }
- else
- {
- FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Created, name, null, this);
- }
- }
-
- return keyspaceMetadata;
+ return GetReplicas(null, partitionKey);
}
///
/// Get the replicas for a given partition key and keyspace
///
- public ICollection GetReplicas(string keyspaceName, byte[] partitionKey)
+ public async Task> GetReplicasAsync(string keyspaceName, byte[] partitionKey)
{
- if (_tokenMap == null)
- {
- Metadata.Logger.Warning("Metadata.GetReplicas was called but there was no token map.");
- return new Host[0];
- }
- return _tokenMap.GetReplicas(keyspaceName, _tokenMap.Factory.Hash(partitionKey));
+ await TryInitializeAsync().ConfigureAwait(false);
+ return _internalMetadata.GetReplicas(keyspaceName, partitionKey);
}
- public ICollection GetReplicas(byte[] partitionKey)
+ ///
+ /// Get the replicas for a given partition key
+ ///
+ public Task> GetReplicasAsync(byte[] partitionKey)
{
- return GetReplicas(null, partitionKey);
+ return GetReplicasAsync(null, partitionKey);
}
///
@@ -345,14 +253,20 @@ public ICollection GetReplicas(byte[] partitionKey)
/// * keyspace is not a known keyspace.
public KeyspaceMetadata GetKeyspace(string keyspace)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- //Use local cache
- _keyspaces.TryGetValue(keyspace, out var ksInfo);
- return ksInfo;
- }
+ return TaskHelper.WaitToComplete(GetKeyspaceAsync(keyspace), _queryAbortTimeout);
+ }
- return TaskHelper.WaitToComplete(SchemaParser.GetKeyspaceAsync(keyspace), _queryAbortTimeout);
+ ///
+ /// Returns metadata of specified keyspace.
+ ///
+ /// the name of the keyspace for which metadata should be
+ /// returned.
+ /// the metadata of the requested keyspace or null if
+ /// * keyspace is not a known keyspace.
+ public async Task GetKeyspaceAsync(string keyspace)
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetKeyspaceAsync(keyspace).ConfigureAwait(false);
}
///
@@ -361,13 +275,17 @@ public KeyspaceMetadata GetKeyspace(string keyspace)
/// a collection of all defined keyspaces names.
public ICollection GetKeyspaces()
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- //Use local cache
- return _keyspaces.Keys;
- }
+ return TaskHelper.WaitToComplete(GetKeyspacesAsync(), _queryAbortTimeout);
+ }
- return TaskHelper.WaitToComplete(SchemaParser.GetKeyspacesNamesAsync(), _queryAbortTimeout);
+ ///
+ /// Returns a collection of all defined keyspaces names.
+ ///
+ /// a collection of all defined keyspaces names.
+ public async Task> GetKeyspacesAsync()
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetKeyspacesAsync().ConfigureAwait(false);
}
///
@@ -379,14 +297,20 @@ public ICollection GetKeyspaces()
/// keyspace.
public ICollection GetTables(string keyspace)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
- ? new string[0]
- : ksMetadata.GetTablesNames();
- }
+ return TaskHelper.WaitToComplete(GetTablesAsync(keyspace), _queryAbortTimeout);
+ }
- return TaskHelper.WaitToComplete(SchemaParser.GetTableNamesAsync(keyspace), _queryAbortTimeout);
+ ///
+ /// Returns names of all tables which are defined within specified keyspace.
+ ///
+ /// the name of the keyspace for which all tables metadata should be
+ /// returned.
+ /// an ICollection of the metadata for the tables defined in this
+ /// keyspace.
+ public async Task> GetTablesAsync(string keyspace)
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetTablesAsync(keyspace).ConfigureAwait(false);
}
///
@@ -400,16 +324,16 @@ public TableMetadata GetTable(string keyspace, string tableName)
return TaskHelper.WaitToComplete(GetTableAsync(keyspace, tableName), _queryAbortTimeout * 2);
}
- internal Task GetTableAsync(string keyspace, string tableName)
+ ///
+ /// Returns TableMetadata for specified table in specified keyspace.
+ ///
+ /// name of the keyspace within specified table is defined.
+ /// name of table for which metadata should be returned.
+ /// a TableMetadata for the specified table in the specified keyspace.
+ public async Task GetTableAsync(string keyspace, string tableName)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
- ? Task.FromResult(null)
- : ksMetadata.GetTableMetadataAsync(tableName);
- }
-
- return SchemaParser.GetTableAsync(keyspace, tableName);
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetTableAsync(keyspace, tableName).ConfigureAwait(false);
}
///
@@ -420,14 +344,19 @@ internal Task GetTableAsync(string keyspace, string tableName)
/// a MaterializedViewMetadata for the view in the specified keyspace.
public MaterializedViewMetadata GetMaterializedView(string keyspace, string name)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
- ? null
- : ksMetadata.GetMaterializedViewMetadata(name);
- }
+ return TaskHelper.WaitToComplete(GetMaterializedViewAsync(keyspace, name), _queryAbortTimeout * 2);
+ }
- return TaskHelper.WaitToComplete(SchemaParser.GetViewAsync(keyspace, name), _queryAbortTimeout * 2);
+ ///
+ /// Returns the view metadata for the provided view name in the keyspace.
+ ///
+ /// name of the keyspace within specified view is defined.
+ /// name of view.
+ /// a MaterializedViewMetadata for the view in the specified keyspace.
+ public async Task GetMaterializedViewAsync(string keyspace, string name)
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetMaterializedViewAsync(keyspace, name).ConfigureAwait(false);
}
///
@@ -441,16 +370,10 @@ public UdtColumnInfo GetUdtDefinition(string keyspace, string typeName)
///
/// Gets the definition associated with a User Defined Type from Cassandra
///
- public Task GetUdtDefinitionAsync(string keyspace, string typeName)
+ public async Task GetUdtDefinitionAsync(string keyspace, string typeName)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
- ? Task.FromResult(null)
- : ksMetadata.GetUdtDefinitionAsync(typeName);
- }
-
- return SchemaParser.GetUdtDefinitionAsync(keyspace, typeName);
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetUdtDefinitionAsync(keyspace, typeName).ConfigureAwait(false);
}
///
@@ -459,15 +382,17 @@ public Task GetUdtDefinitionAsync(string keyspace, string typeNam
/// The function metadata or null if not found.
public FunctionMetadata GetFunction(string keyspace, string name, string[] signature)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
- ? null
- : ksMetadata.GetFunction(name, signature);
- }
+ return TaskHelper.WaitToComplete(GetFunctionAsync(keyspace, name, signature), _queryAbortTimeout);
+ }
- var signatureString = SchemaParser.ComputeFunctionSignatureString(signature);
- return TaskHelper.WaitToComplete(SchemaParser.GetFunctionAsync(keyspace, name, signatureString), _queryAbortTimeout);
+ ///
+ /// Gets the definition associated with a User Defined Function from Cassandra
+ ///
+ /// The function metadata or null if not found.
+ public async Task GetFunctionAsync(string keyspace, string name, string[] signature)
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetFunctionAsync(keyspace, name, signature).ConfigureAwait(false);
}
///
@@ -476,15 +401,17 @@ public FunctionMetadata GetFunction(string keyspace, string name, string[] signa
/// The aggregate metadata or null if not found.
public AggregateMetadata GetAggregate(string keyspace, string name, string[] signature)
{
- if (Configuration.MetadataSyncOptions.MetadataSyncEnabled)
- {
- return !_keyspaces.TryGetValue(keyspace, out var ksMetadata)
- ? null
- : ksMetadata.GetAggregate(name, signature);
- }
+ return TaskHelper.WaitToComplete(GetAggregateAsync(keyspace, name, signature), _queryAbortTimeout);
+ }
- var signatureString = SchemaParser.ComputeFunctionSignatureString(signature);
- return TaskHelper.WaitToComplete(SchemaParser.GetAggregateAsync(keyspace, name, signatureString), _queryAbortTimeout);
+ ///
+ /// Gets the definition associated with a aggregate from Cassandra
+ ///
+ /// The aggregate metadata or null if not found.
+ public async Task GetAggregateAsync(string keyspace, string name, string[] signature)
+ {
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetAggregateAsync(keyspace, name, signature).ConfigureAwait(false);
}
///
@@ -492,99 +419,29 @@ public AggregateMetadata GetAggregate(string keyspace, string name, string[] sig
///
/// The query trace that contains the id, which properties are going to be populated.
///
- internal Task GetQueryTraceAsync(QueryTrace trace)
+ internal async Task GetQueryTraceAsync(QueryTrace trace)
{
- return _schemaParser.GetQueryTraceAsync(trace, Configuration.Timer);
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.GetQueryTraceAsync(trace).ConfigureAwait(false);
}
-
+
///
/// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
/// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
///
public bool RefreshSchema(string keyspace = null, string table = null)
{
- return TaskHelper.WaitToComplete(RefreshSchemaAsync(keyspace, table), Configuration.DefaultRequestOptions.QueryAbortTimeout * 2);
+ return TaskHelper.WaitToComplete(RefreshSchemaAsync(keyspace, table), _queryAbortTimeout * 2);
}
-
+
///
/// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
/// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
///
public async Task RefreshSchemaAsync(string keyspace = null, string table = null)
{
- if (keyspace == null)
- {
- await ControlConnection.ScheduleAllKeyspacesRefreshAsync(true).ConfigureAwait(false);
- return true;
- }
-
- await ControlConnection.ScheduleKeyspaceRefreshAsync(keyspace, true).ConfigureAwait(false);
- _keyspaces.TryGetValue(keyspace, out var ks);
- if (ks == null)
- {
- return false;
- }
-
- if (table != null)
- {
- ks.ClearTableMetadata(table);
- }
- return true;
- }
-
- ///
- /// this method should be called by the event debouncer
- ///
- internal bool RemoveKeyspace(string name)
- {
- var existed = RemoveKeyspaceFromTokenMap(name);
- if (!existed)
- {
- return false;
- }
-
- FireSchemaChangedEvent(SchemaChangedEventArgs.Kind.Dropped, name, null, this);
- return true;
- }
-
- ///
- /// this method should be called by the event debouncer
- ///
- internal Task RefreshSingleKeyspace(string name)
- {
- return UpdateTokenMapForKeyspace(name);
- }
-
- internal void ClearTable(string keyspaceName, string tableName)
- {
- if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
- {
- ksMetadata.ClearTableMetadata(tableName);
- }
- }
-
- internal void ClearView(string keyspaceName, string name)
- {
- if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
- {
- ksMetadata.ClearViewMetadata(name);
- }
- }
-
- internal void ClearFunction(string keyspaceName, string functionName, string[] signature)
- {
- if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
- {
- ksMetadata.ClearFunction(functionName, signature);
- }
- }
-
- internal void ClearAggregate(string keyspaceName, string aggregateName, string[] signature)
- {
- if (_keyspaces.TryGetValue(keyspaceName, out var ksMetadata))
- {
- ksMetadata.ClearAggregate(aggregateName, signature);
- }
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.RefreshSchemaAsync(keyspace, table).ConfigureAwait(false);
}
///
@@ -599,128 +456,8 @@ internal void ClearAggregate(string keyspaceName, string aggregateName, string[]
/// True if schema agreement was successful and false if it was not successful.
public async Task CheckSchemaAgreementAsync()
{
- if (Hosts.Count == 1)
- {
- // If there is just one node, the schema is up to date in all nodes :)
- return true;
- }
-
- try
- {
- var queries = new[]
- {
- ControlConnection.QueryAsync(SelectSchemaVersionLocal),
- ControlConnection.QueryAsync(SelectSchemaVersionPeers)
- };
-
- await Task.WhenAll(queries).ConfigureAwait(false);
-
- return CheckSchemaVersionResults(queries[0].Result, queries[1].Result);
- }
- catch (Exception ex)
- {
- Logger.Error("Error while checking schema agreement.", ex);
- }
-
- return false;
- }
-
- ///
- /// Checks if there is only one schema version between the provided query results.
- ///
- ///
- /// Results obtained from a query to system.local table.
- /// Must contain the schema_version column.
- ///
- ///
- /// Results obtained from a query to system.peers table.
- /// Must contain the schema_version column.
- ///
- /// True if there is a schema agreement (only 1 schema version). False otherwise.
- private static bool CheckSchemaVersionResults(
- IEnumerable localVersionQuery, IEnumerable peerVersionsQuery)
- {
- return new HashSet(
- peerVersionsQuery
- .Concat(localVersionQuery)
- .Select(r => r.GetValue("schema_version"))).Count == 1;
- }
-
- ///
- /// Waits until that the schema version in all nodes is the same or the waiting time passed.
- /// This method blocks the calling thread.
- ///
- internal async Task WaitForSchemaAgreementAsync(IConnection connection)
- {
- if (Hosts.Count == 1)
- {
- //If there is just one node, the schema is up to date in all nodes :)
- return true;
- }
- var start = DateTime.Now;
- var waitSeconds = Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds;
- Metadata.Logger.Info("Waiting for schema agreement");
- try
- {
- var totalVersions = 0;
- while (DateTime.Now.Subtract(start).TotalSeconds < waitSeconds)
- {
- var serializer = _serializerManager.GetCurrentSerializer();
- var schemaVersionLocalQuery =
- new QueryRequest(
- serializer,
- Metadata.SelectSchemaVersionLocal,
- QueryProtocolOptions.Default,
- false,
- null);
- var schemaVersionPeersQuery =
- new QueryRequest(
- serializer,
- Metadata.SelectSchemaVersionPeers,
- QueryProtocolOptions.Default,
- false,
- null);
- var queries = new[] { connection.Send(schemaVersionLocalQuery), connection.Send(schemaVersionPeersQuery) };
- // ReSharper disable once CoVariantArrayConversion
- await Task.WhenAll(queries);
-
- if (Metadata.CheckSchemaVersionResults(
- Configuration.MetadataRequestHandler.GetRowSet(await queries[0].ConfigureAwait(false)),
- Configuration.MetadataRequestHandler.GetRowSet(await queries[1].ConfigureAwait(false))))
- {
- return true;
- }
-
- await Task.Delay(500).ConfigureAwait(false);
- }
- Metadata.Logger.Info($"Waited for schema agreement, still {totalVersions} schema versions in the cluster.");
- }
- catch (Exception ex)
- {
- //Exceptions are not fatal
- Metadata.Logger.Error("There was an exception while trying to retrieve schema versions", ex);
- }
-
- return false;
- }
-
- ///
- /// Sets the Cassandra version in order to identify how to parse the metadata information
- ///
- ///
- internal void SetCassandraVersion(Version version)
- {
- _schemaParser = Configuration.SchemaParserFactory.Create(version, this, GetUdtDefinitionAsync, _schemaParser);
- }
-
- internal void SetProductTypeAsDbaas()
- {
- IsDbaas = true;
- }
-
- internal IEnumerable UpdateResolvedContactPoint(IContactPoint contactPoint, IEnumerable endpoints)
- {
- return _resolvedContactPoints.AddOrUpdate(contactPoint, _ => endpoints, (_, __) => endpoints);
+ await TryInitializeAsync().ConfigureAwait(false);
+ return await _internalMetadata.CheckSchemaAgreementAsync().ConfigureAwait(false);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs b/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs
index 5c4cb0089..6848dba10 100644
--- a/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs
+++ b/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs
@@ -15,12 +15,13 @@
using System;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
namespace Cassandra.MetadataHelpers
{
internal interface ISchemaParserFactory
{
- ISchemaParser Create(Version cassandraVersion, Metadata parent,
+ ISchemaParser Create(Version cassandraVersion, InternalMetadata parent,
Func> udtResolver,
ISchemaParser currentInstance = null);
}
diff --git a/src/Cassandra/Policies/ILocalDatacenterProvider.cs b/src/Cassandra/Policies/ILocalDatacenterProvider.cs
index 1a9bba535..7be66d2d0 100644
--- a/src/Cassandra/Policies/ILocalDatacenterProvider.cs
+++ b/src/Cassandra/Policies/ILocalDatacenterProvider.cs
@@ -13,6 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra
@@ -30,6 +31,6 @@ internal interface ILocalDatacenterProvider
///
/// Should be called after we have an initialized cluster instance.
///
- void Initialize(IInternalCluster cluster, Metadata metadata);
+ void Initialize(IInternalCluster cluster, IInternalMetadata internalMetadata);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Policies/LocalDatacenterProvider.cs b/src/Cassandra/Policies/LocalDatacenterProvider.cs
index b35da2d98..19adbe7eb 100644
--- a/src/Cassandra/Policies/LocalDatacenterProvider.cs
+++ b/src/Cassandra/Policies/LocalDatacenterProvider.cs
@@ -16,7 +16,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
-
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra
@@ -26,16 +26,16 @@ internal class LocalDatacenterProvider : ILocalDatacenterProvider
private volatile bool _initialized = false;
private volatile IInternalCluster _cluster;
- private volatile Metadata _metadata;
+ private volatile InternalMetadata _internalMetadata;
private volatile IEnumerable _availableDcs;
private volatile string _availableDcsStr;
private volatile string _cachedDatacenter;
- public void Initialize(IInternalCluster cluster, Metadata metadata)
+ public void Initialize(IInternalCluster cluster, InternalMetadata internalMetadata)
{
_cluster = cluster;
- _metadata = metadata;
- _availableDcs = metadata.AllHosts().Select(h => h.Datacenter).Where(dc => dc != null).Distinct().ToList();
+ _internalMetadata = internalMetadata;
+ _availableDcs = internalMetadata.AllHosts().Select(h => h.Datacenter).Where(dc => dc != null).Distinct().ToList();
_availableDcsStr = string.Join(", ", _availableDcs);
_initialized = true;
}
@@ -89,7 +89,7 @@ private string ValidateAndReturnDatacenter(string datacenter)
private string InferLocalDatacenter()
{
- var cc = _metadata.ControlConnection;
+ var cc = _internalMetadata.ControlConnection;
if (cc == null)
{
throw new DriverInternalError("ControlConnection was not correctly set");
diff --git a/src/Cassandra/Requests/IPrepareHandler.cs b/src/Cassandra/Requests/IPrepareHandler.cs
index 2e0c92a21..de15aa1fc 100644
--- a/src/Cassandra/Requests/IPrepareHandler.cs
+++ b/src/Cassandra/Requests/IPrepareHandler.cs
@@ -24,6 +24,6 @@ namespace Cassandra.Requests
internal interface IPrepareHandler
{
Task PrepareAsync(
- PrepareRequest request, IInternalSession session, Metadata metadata, IEnumerator queryPlan);
+ PrepareRequest request, IInternalSession session, IEnumerator queryPlan);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Requests/PrepareHandler.cs b/src/Cassandra/Requests/PrepareHandler.cs
index dcc726758..5c355f458 100644
--- a/src/Cassandra/Requests/PrepareHandler.cs
+++ b/src/Cassandra/Requests/PrepareHandler.cs
@@ -20,7 +20,9 @@
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
+
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.Responses;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
@@ -43,9 +45,9 @@ public PrepareHandler(ISerializerManager serializerManager, IInternalCluster clu
}
public async Task PrepareAsync(
- PrepareRequest request, IInternalSession session, Metadata metadata, IEnumerator queryPlan)
+ PrepareRequest request, IInternalSession session, IEnumerator queryPlan)
{
- var prepareResult = await SendRequestToOneNode(session, metadata, queryPlan, request).ConfigureAwait(false);
+ var prepareResult = await SendRequestToOneNode(session, queryPlan, request).ConfigureAwait(false);
if (session.Cluster.Configuration.QueryOptions.IsPrepareOnAllHosts())
{
@@ -56,7 +58,7 @@ public async Task PrepareAsync(
}
private async Task SendRequestToOneNode(
- IInternalSession session, Metadata metadata, IEnumerator queryPlan, PrepareRequest request)
+ IInternalSession session, IEnumerator queryPlan, PrepareRequest request)
{
var triedHosts = new Dictionary();
@@ -71,9 +73,9 @@ private async Task SendRequestToOneNode(
var result = await connection.Send(request).ConfigureAwait(false);
return new PrepareResult
{
- PreparedStatement =
+ PreparedStatement =
await GetPreparedStatement(
- result, request, request.Keyspace ?? connection.Keyspace, metadata).ConfigureAwait(false),
+ result, request, request.Keyspace ?? connection.Keyspace).ConfigureAwait(false),
TriedHosts = triedHosts,
HostAddress = host.Address
};
@@ -130,7 +132,7 @@ private Host GetNextHost(IEnumerator queryPlan, out HostDistance distance)
}
private async Task GetPreparedStatement(
- Response response, PrepareRequest request, string keyspace, Metadata metadata)
+ Response response, PrepareRequest request, string keyspace)
{
if (response == null)
{
@@ -148,20 +150,20 @@ private async Task GetPreparedStatement(
}
var prepared = (OutputPrepared)output;
var ps = new PreparedStatement(
- prepared.VariablesRowsMetadata,
- prepared.QueryId,
+ prepared.VariablesRowsMetadata,
+ prepared.QueryId,
new ResultMetadata(prepared.ResultMetadataId, prepared.ResultRowsMetadata),
- request.Query,
- keyspace,
+ request.Query,
+ keyspace,
_serializerManager)
{
IncomingPayload = resultResponse.CustomPayload
};
- await FillRoutingInfo(ps, metadata).ConfigureAwait(false);
+ await FillRoutingInfo(ps, _cluster.InternalMetadata).ConfigureAwait(false);
return ps;
}
- private static async Task FillRoutingInfo(PreparedStatement ps, Metadata metadata)
+ private static async Task FillRoutingInfo(PreparedStatement ps, InternalMetadata internalMetadata)
{
var column = ps.Variables.Columns.FirstOrDefault();
if (column?.Keyspace == null)
@@ -184,7 +186,7 @@ private static async Task FillRoutingInfo(PreparedStatement ps, Metadata metadat
try
{
const string msgRoutingNotSet = "Routing information could not be set for query \"{0}\"";
- var table = await metadata.GetTableAsync(column.Keyspace, column.Table).ConfigureAwait(false);
+ var table = await internalMetadata.GetTableAsync(column.Keyspace, column.Table).ConfigureAwait(false);
if (table == null)
{
Logger.Info(msgRoutingNotSet, ps.Cql);
diff --git a/src/Cassandra/SessionManagement/IInternalCluster.cs b/src/Cassandra/SessionManagement/IInternalCluster.cs
index 430d2dc52..ba53b7e82 100644
--- a/src/Cassandra/SessionManagement/IInternalCluster.cs
+++ b/src/Cassandra/SessionManagement/IInternalCluster.cs
@@ -30,6 +30,8 @@ internal interface IInternalCluster : ICluster
{
ISerializerManager SerializerManager { get; }
+ IInternalMetadata InternalMetadata { get; }
+
bool AnyOpenConnections(Host host);
///
@@ -61,8 +63,8 @@ internal interface IInternalCluster : ICluster
HostDistance RetrieveAndSetDistance(Host host);
///
- /// Initializes once (Thread-safe) the control connection and returns the metadata associated with the Cluster instance
+ /// Initializes once (Thread-safe) the control connection
///
- Task TryInitAndGetMetadataAsync();
+ Task TryInitializeAsync();
}
}
\ No newline at end of file
From 0bd3a0335b4e292af90aae65330145012dac10fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Tue, 30 Jun 2020 18:56:50 +0100
Subject: [PATCH 09/31] revamp Metadata
- add IMetadata interface and change Metadata to be internal
- add *Async methods to IMetadata
- change internal references from Metadata to InternalMetadata
- add *Snapshot methods to IMetadata
---
src/Cassandra/Builder.cs | 4 +-
src/Cassandra/Cluster.cs | 99 ++--
src/Cassandra/ClusterDescription.cs | 5 +-
src/Cassandra/Configuration.cs | 15 +-
.../Control/ProtocolVersionNegotiator.cs | 6 +-
src/Cassandra/Data/Linq/SessionExtensions.cs | 2 +-
src/Cassandra/Data/Linq/Table.cs | 23 +-
src/Cassandra/DataStax/Graph/GraphOptions.cs | 2 +-
.../DataStax/Insights/IInsightsClient.cs | 3 +-
.../InfoProviders/IInsightsInfoProvider.cs | 3 +-
.../AuthProviderInfoProvider.cs | 3 +-
.../ConfigAntiPatternsInfoProvider.cs | 31 +-
.../StartupMessage/DataCentersInfoProvider.cs | 10 +-
.../StartupMessage/DriverInfoProvider.cs | 3 +-
.../ExecutionProfileInfoProvider.cs | 6 +-
.../StartupMessage/HostnameInfoProvider.cs | 6 +-
.../OtherOptionsInfoProvider.cs | 7 +-
.../StartupMessage/PlatformInfoProvider.cs | 5 +-
.../PoolSizeByHostDistanceInfoProvider.cs | 8 +-
.../ReconnectionPolicyInfoProvider.cs | 19 +-
.../StatusMessage/NodeStatusInfoProvider.cs | 6 +-
.../DataStax/Insights/InsightsClient.cs | 48 +-
.../IInsightsMessageFactory.cs | 5 +-
.../InsightsStartupMessageFactory.cs | 40 +-
.../InsightsStatusMessageFactory.cs | 8 +-
src/Cassandra/Extensions.cs | 15 +-
src/Cassandra/ICluster.cs | 2 +-
src/Cassandra/IMetadata.cs | 274 +++++++++++
src/Cassandra/ISession.cs | 11 +-
src/Cassandra/KeyspaceMetadata.cs | 7 +-
src/Cassandra/Metadata.cs | 425 ++++++++----------
.../MetadataHelpers/ISchemaParserFactory.cs | 2 +-
.../MetadataHelpers/SchemaParserFactory.cs | 3 +-
.../ConstantSpeculativeExecutionPolicy.cs | 4 +-
.../Policies/DCAwareRoundRobinPolicy.cs | 8 +-
.../Policies/DefaultLoadBalancingPolicy.cs | 6 +-
.../Policies/ILoadBalancingPolicy.cs | 4 +-
.../Policies/ISpeculativeExecutionPolicy.cs | 5 +-
.../Policies/LocalDatacenterProvider.cs | 4 +-
.../Policies/NoSpeculativeExecutionPolicy.cs | 4 +-
.../Policies/RetryLoadBalancingPolicy.cs | 4 +-
src/Cassandra/Policies/RoundRobinPolicy.cs | 6 +-
src/Cassandra/Policies/TokenAwarePolicy.cs | 8 +-
src/Cassandra/ProtocolVersion.cs | 13 +-
src/Cassandra/QueryTrace.cs | 7 +-
src/Cassandra/Requests/IRequestHandler.cs | 3 +-
.../Requests/IRequestHandlerFactory.cs | 7 +-
src/Cassandra/Requests/PrepareHandler.cs | 2 +-
src/Cassandra/Requests/ReprepareHandler.cs | 2 +-
src/Cassandra/Requests/RequestExecution.cs | 6 +-
src/Cassandra/Requests/RequestHandler.cs | 23 +-
.../Requests/RequestHandlerFactory.cs | 7 +-
src/Cassandra/SchemaParser.cs | 11 +-
src/Cassandra/Session.cs | 71 +--
.../SessionManagement/IInternalCluster.cs | 15 +-
.../SessionManagement/IInternalSession.cs | 5 +-
src/Cassandra/SessionState.cs | 5 +-
src/Cassandra/UdtMappingDefinitions.cs | 6 +-
58 files changed, 808 insertions(+), 534 deletions(-)
create mode 100644 src/Cassandra/IMetadata.cs
diff --git a/src/Cassandra/Builder.cs b/src/Cassandra/Builder.cs
index 894718cda..6d540c8a7 100644
--- a/src/Cassandra/Builder.cs
+++ b/src/Cassandra/Builder.cs
@@ -1050,7 +1050,7 @@ public Builder WithExecutionProfiles(Action profileOpt
/// specifies what is the default for each option.
///
///
- /// In case you disable Metadata synchronization, please ensure you invoke (accessible via ) in order to keep the token metadata up to date
+ /// In case you disable Metadata synchronization, please ensure you invoke (accessible via ) in order to keep the token metadata up to date
/// otherwise you will not be getting everything you can out of token aware routing, i.e. , which is enabled by the default.
///
///
@@ -1061,7 +1061,7 @@ public Builder WithExecutionProfiles(Action profileOpt
///
/// Token metadata will not be computed and stored.
/// This means that token aware routing (, enabled by default) will only work correctly
- /// if you keep the token metadata up to date using the method (accessible via ).
+ /// if you keep the token metadata up to date using the method (accessible via ).
/// If you wish to go this route of manually refreshing the metadata then
/// it's recommended to refresh only the keyspaces that this application will use, by passing the keyspace parameter.
///
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 503f46ad0..1d36ea1ea 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -28,9 +28,7 @@
using Cassandra.Connections;
using Cassandra.Connections.Control;
using Cassandra.Helpers;
-using Cassandra.ProtocolEvents;
using Cassandra.Requests;
-using Cassandra.Serialization;
using Cassandra.SessionManagement;
using Cassandra.Tasks;
@@ -45,30 +43,27 @@ public class Cluster : IInternalCluster
private static readonly IPEndPoint DefaultContactPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9042);
- private static ProtocolVersion _maxProtocolVersion = ProtocolVersion.MaxSupported;
internal static readonly Logger Logger = new Logger(typeof(Cluster));
private readonly CopyOnWriteList _connectedSessions = new CopyOnWriteList();
- private readonly SerializerManager _serializerManager;
private readonly bool _implicitContactPoint = false;
private volatile Exception _initException;
private readonly SemaphoreSlim _sessionCreateLock = new SemaphoreSlim(1, 1);
private long _sessionCounter = -1;
private readonly IInternalMetadata _internalMetadata;
+ private readonly Metadata _lazyMetadata;
private IReadOnlyList _loadBalancingPolicies;
- private readonly Task _initTask;
+ private readonly Task _initTask;
private long _state = Cluster.Initializing;
-
+
internal IInternalCluster InternalRef => this;
IInternalMetadata IInternalCluster.InternalMetadata => _internalMetadata;
-
+
private bool IsDisposed => Interlocked.Read(ref _state) == Cluster.Disposed;
- ISerializerManager IInternalCluster.SerializerManager => _serializerManager;
-
Exception IInternalCluster.InitException => _initException;
///
@@ -112,53 +107,20 @@ public static Builder Builder()
return new Builder();
}
- ///
- /// Gets or sets the maximum protocol version used by this driver.
- ///
- /// While property value is maintained for backward-compatibility,
- /// use to set the maximum protocol version used by the driver.
- ///
- ///
- /// Protocol version used can not be higher than .
- ///
- ///
- public static int MaxProtocolVersion
- {
- get { return (int)_maxProtocolVersion; }
- set
- {
- if (value > (int)ProtocolVersion.MaxSupported)
- {
- // Ignore
- return;
- }
- _maxProtocolVersion = (ProtocolVersion)value;
- }
- }
-
///
- public Configuration Configuration { get; private set; }
+ public Configuration Configuration { get; }
///
// ReSharper disable once ConvertToAutoProperty, reviewed
bool IInternalCluster.ImplicitContactPoint => _implicitContactPoint;
///
- public Metadata Metadata { get; }
-
+ public IMetadata Metadata => _lazyMetadata;
+
private Cluster(IEnumerable contactPoints, Configuration configuration)
{
Configuration = configuration;
- var protocolVersion = _maxProtocolVersion;
- if (Configuration.ProtocolOptions.MaxProtocolVersionValue != null &&
- Configuration.ProtocolOptions.MaxProtocolVersionValue.Value.IsSupported(configuration))
- {
- protocolVersion = Configuration.ProtocolOptions.MaxProtocolVersionValue.Value;
- }
-
- _serializerManager = new SerializerManager(protocolVersion, configuration.TypeSerializers);
-
var contactPointsList = contactPoints.ToList();
if (contactPointsList.Count == 0)
{
@@ -168,15 +130,15 @@ private Cluster(IEnumerable contactPoints, Configuration configuration)
}
var parsedContactPoints = configuration.ContactPointParser.ParseContactPoints(contactPointsList);
-
- Metadata = new Metadata(this, configuration, _serializerManager, parsedContactPoints);
- _internalMetadata = Metadata.InternalMetadata;
+
+ _lazyMetadata = new Metadata(this, configuration, Configuration.SerializerManager, parsedContactPoints);
+ _internalMetadata = _lazyMetadata.InternalMetadata;
_initTask = Task.Run(InitAsync);
}
///
- Task IInternalCluster.TryInitializeAsync()
+ Task IInternalCluster.TryInitAndGetMetadataAsync()
{
var currentState = Interlocked.Read(ref _state);
if (currentState == Cluster.Initialized)
@@ -189,7 +151,7 @@ Task IInternalCluster.TryInitializeAsync()
{
throw new ObjectDisposedException("This cluster object has been disposed.");
}
-
+
if (_initException != null)
{
//There was an exception that is not possible to recover from
@@ -199,7 +161,7 @@ Task IInternalCluster.TryInitializeAsync()
return _initTask;
}
- private async Task InitAsync()
+ private async Task InitAsync()
{
Cluster.Logger.Info("Connecting to cluster using {0}", Cluster.GetAssemblyInfo());
try
@@ -218,7 +180,7 @@ private async Task InitAsync()
// Only abort the async operations when at least twice the time for ConnectTimeout per host passed
var initialAbortTimeout = Configuration.SocketOptions.ConnectTimeoutMillis * 2 * _internalMetadata.Hosts.Count;
initialAbortTimeout = Math.Max(initialAbortTimeout, Configuration.SocketOptions.MetadataAbortTimeout);
- var initTask = Metadata.TryInitializeAsync();
+ var initTask = _lazyMetadata.TryInitializeAsync();
try
{
await initTask.WaitToCompleteAsync(initialAbortTimeout).ConfigureAwait(false);
@@ -268,11 +230,15 @@ private async Task InitAsync()
// Set metadata dependent options
SetMetadataDependentOptions();
- Cluster.Logger.Info("Cluster Connected using binary protocol version: [" + _serializerManager.CurrentProtocolVersion + "]");
+ Cluster.Logger.Info("Cluster Connected using binary protocol version: ["
+ + Configuration.SerializerManager.CurrentProtocolVersion
+ + "]");
_internalMetadata.Hosts.Added += _internalMetadata.OnHostAdded;
_internalMetadata.Hosts.Removed += _internalMetadata.OnHostRemoved;
_internalMetadata.Hosts.Up += OnHostUp;
Cluster.Logger.Info("Cluster [" + _internalMetadata.ClusterName + "] has been initialized.");
+
+ return _internalMetadata;
}
catch (NoHostAvailableException)
{
@@ -312,7 +278,7 @@ IReadOnlyDictionary> IInternalCl
{
return _internalMetadata.ResolvedContactPoints;
}
-
+
///
/// Creates a new session on this cluster.
///
@@ -358,7 +324,6 @@ public async Task ConnectAsync(string keyspace)
_connectedSessions.Add(session);
Cluster.Logger.Info("Session connected ({0})", session.GetHashCode());
return session;
-
}
finally
{
@@ -441,7 +406,7 @@ private async void OnHostUp(Host h)
"that came UP:" + Environment.NewLine + "{1}", h?.Address?.ToString(), ex.ToString());
}
}
-
+
///
public void Shutdown(int timeoutMs = Timeout.Infinite)
{
@@ -478,7 +443,7 @@ public async Task ShutdownAsync(int timeoutMs = Timeout.Infinite)
{
Cluster.Logger.Warning("Exception occured while disposing session instances: {0}", ex.ToString());
}
-
+
if (previousState != Cluster.Initialized)
{
return;
@@ -491,7 +456,7 @@ public async Task ShutdownAsync(int timeoutMs = Timeout.Infinite)
private async Task ShutdownInternalAsync()
{
- await Metadata.ShutdownAsync().ConfigureAwait(false);
+ await _lazyMetadata.ShutdownAsync().ConfigureAwait(false);
Configuration.Timer.Dispose();
// Dispose policies
@@ -530,7 +495,7 @@ HostDistance IInternalCluster.RetrieveAndSetDistance(Host host)
async Task IInternalCluster.PrepareAsync(
IInternalSession session, string cqlQuery, string keyspace, IDictionary customPayload)
{
- await InternalRef.TryInitializeAsync().ConfigureAwait(false);
+ await InternalRef.TryInitAndGetMetadataAsync().ConfigureAwait(false);
var serializer = _internalMetadata.SerializerManager.GetCurrentSerializer();
var currentVersion = serializer.ProtocolVersion;
@@ -549,9 +514,9 @@ async Task IInternalCluster.PrepareAsync(
private async Task PrepareAsync(IInternalSession session, PrepareRequest request)
{
var lbp = session.Cluster.Configuration.DefaultRequestOptions.LoadBalancingPolicy;
- var handler = InternalRef.Configuration.PrepareHandlerFactory.CreatePrepareHandler(_serializerManager, this);
+ var handler = InternalRef.Configuration.PrepareHandlerFactory.CreatePrepareHandler(Configuration.SerializerManager, this);
var ps = await handler.PrepareAsync(
- request,
+ request,
session,
lbp.NewQueryPlan(Metadata, session.Keyspace, null).GetEnumerator()).ConfigureAwait(false);
var psAdded = InternalRef.PreparedQueries.GetOrAdd(ps.Id, ps);
@@ -574,7 +539,7 @@ private async Task ReprepareAllQueries(Host host)
{
return;
}
-
+
// Get the first pool for that host that has open connections
var pool = sessions.Select(s => s.GetExistingPool(host.Address)).Where(p => p != null).FirstOrDefault(p => p.HasConnections);
if (pool == null)
@@ -586,7 +551,7 @@ private async Task ReprepareAllQueries(Host host)
PrepareHandler.Logger.Info($"Re-preparing {preparedQueries.Count} queries on {host.Address}");
var tasks = new List(preparedQueries.Count);
var handler = InternalRef.Configuration.PrepareHandlerFactory.CreateReprepareHandler();
- var serializer = _serializerManager.GetCurrentSerializer();
+ var serializer = Configuration.SerializerManager.GetCurrentSerializer();
using (var semaphore = new SemaphoreSlim(64, 64))
{
foreach (var ps in preparedQueries)
@@ -594,10 +559,10 @@ private async Task ReprepareAllQueries(Host host)
var request = new PrepareRequest(serializer, ps.Cql, ps.Keyspace, null);
await semaphore.WaitAsync().ConfigureAwait(false);
tasks.Add(Task.Run(() => handler.ReprepareOnSingleNodeAsync(
- new KeyValuePair(host, pool),
- ps,
- request,
- semaphore,
+ new KeyValuePair(host, pool),
+ ps,
+ request,
+ semaphore,
true)));
}
diff --git a/src/Cassandra/ClusterDescription.cs b/src/Cassandra/ClusterDescription.cs
index 7cf4b4e52..44de74e8f 100644
--- a/src/Cassandra/ClusterDescription.cs
+++ b/src/Cassandra/ClusterDescription.cs
@@ -17,11 +17,12 @@
namespace Cassandra
{
///
- /// Provides a snapshot of cluster metadata properties.
+ /// Provides a snapshot of cluster metadata properties.
///
public class ClusterDescription
{
- public ClusterDescription(string clusterName, bool isDbaas, ProtocolVersion protocolVersion)
+ internal ClusterDescription(
+ string clusterName, bool isDbaas, ProtocolVersion protocolVersion)
{
ClusterName = clusterName;
IsDbaas = isDbaas;
diff --git a/src/Cassandra/Configuration.cs b/src/Cassandra/Configuration.cs
index e1669f869..516cb689f 100644
--- a/src/Cassandra/Configuration.cs
+++ b/src/Cassandra/Configuration.cs
@@ -56,6 +56,8 @@ public class Configuration
internal const string DefaultExecutionProfileName = "default";
internal const string DefaultSessionName = "s";
+ internal static readonly ProtocolVersion MaxProtocolVersion = ProtocolVersion.MaxSupported;
+
///
/// Gets the policies set for the cluster.
///
@@ -140,6 +142,8 @@ public class Configuration
///
internal IEnumerable TypeSerializers { get; set; }
+ internal ISerializerManager SerializerManager { get; }
+
internal MetadataSyncOptions MetadataSyncOptions { get; }
internal IStartupOptionsFactory StartupOptionsFactory { get; }
@@ -420,12 +424,21 @@ internal Configuration(Policies policies,
EndPointResolver = endPointResolver ?? new EndPointResolver(ServerNameResolver);
ContactPointParser = contactPointParser ?? new ContactPointParser(DnsResolver, ProtocolOptions, ServerNameResolver, KeepContactPointsUnresolved);
LocalDatacenterProvider = localDatacenterProvider ?? new LocalDatacenterProvider();
-
+
// Create the buffer pool with 16KB for small buffers and 256Kb for large buffers.
// The pool does not eagerly reserve the buffers, so it doesn't take unnecessary memory
// to create the instance.
BufferPool = new RecyclableMemoryStreamManager(16 * 1024, 256 * 1024, ProtocolOptions.MaximumFrameLength);
Timer = new HashedWheelTimer();
+
+ var protocolVersion = Configuration.MaxProtocolVersion;
+ if (ProtocolOptions.MaxProtocolVersionValue != null &&
+ ProtocolOptions.MaxProtocolVersionValue.Value.IsSupported(AllowBetaProtocolVersions))
+ {
+ protocolVersion = ProtocolOptions.MaxProtocolVersionValue.Value;
+ }
+
+ SerializerManager = new SerializerManager(protocolVersion, TypeSerializers);
}
///
diff --git a/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs b/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs
index 81b542a33..2e2127d49 100644
--- a/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs
+++ b/src/Cassandra/Connections/Control/ProtocolVersionNegotiator.cs
@@ -26,7 +26,7 @@ public async Task NegotiateVersionAsync(
IConnection connection,
ISerializerManager serializer)
{
- var commonVersion = serializer.CurrentProtocolVersion.GetHighestCommon(config, internalMetadata.Hosts);
+ var commonVersion = serializer.CurrentProtocolVersion.GetHighestCommon(config.AllowBetaProtocolVersions, internalMetadata.Hosts);
if (commonVersion != serializer.CurrentProtocolVersion)
{
// Current connection will be closed and reopened
@@ -45,9 +45,9 @@ public async Task ChangeProtocolVersion(
UnsupportedProtocolVersionException ex = null,
ProtocolVersion? previousVersion = null)
{
- if (!nextVersion.IsSupported(config) || nextVersion == previousVersion)
+ if (!nextVersion.IsSupported(config.AllowBetaProtocolVersions) || nextVersion == previousVersion)
{
- nextVersion = nextVersion.GetLowerSupported(config);
+ nextVersion = nextVersion.GetLowerSupported(config.AllowBetaProtocolVersions);
}
if (nextVersion == 0)
diff --git a/src/Cassandra/Data/Linq/SessionExtensions.cs b/src/Cassandra/Data/Linq/SessionExtensions.cs
index 1e6e8f232..57741aac6 100644
--- a/src/Cassandra/Data/Linq/SessionExtensions.cs
+++ b/src/Cassandra/Data/Linq/SessionExtensions.cs
@@ -77,7 +77,7 @@ public static Task CreateBatchAsync(this ISession session)
public static async Task CreateBatchAsync(this ISession session, BatchType batchType)
{
- var metadata = await session.Cluster.GetMetadataAsync().ConfigureAwait(false);
+ var metadata = await session.Cluster.Metadata.GetClusterDescriptionAsync().ConfigureAwait(false);
if (metadata.ProtocolVersion.SupportsBatch())
{
return new BatchV2(session, batchType);
diff --git a/src/Cassandra/Data/Linq/Table.cs b/src/Cassandra/Data/Linq/Table.cs
index f3c3633fd..c63a9a80d 100644
--- a/src/Cassandra/Data/Linq/Table.cs
+++ b/src/Cassandra/Data/Linq/Table.cs
@@ -18,9 +18,9 @@
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
+
using Cassandra.Mapping;
using Cassandra.Mapping.Statements;
-using Cassandra.Tasks;
namespace Cassandra.Data.Linq
{
@@ -69,10 +69,10 @@ public Table(ISession session, MappingConfiguration config, string tableName, st
_keyspaceName = keyspaceName;
//In case no mapping has been defined for the type, determine if the attributes used are Linq or Cassandra.Mapping
//Linq attributes are marked as Obsolete
- #pragma warning disable 612
+#pragma warning disable 612
config.MapperFactory.PocoDataFactory.AddDefinitionDefault(typeof(TEntity),
() => LinqAttributeBasedTypeDefinition.DetermineAttributes(typeof(TEntity)));
- #pragma warning restore 612
+#pragma warning restore 612
var pocoData = config.MapperFactory.GetPocoData();
InternalInitialize(Expression.Constant(this), this, config.MapperFactory, config.StatementFactory, pocoData);
}
@@ -91,7 +91,6 @@ public Table(ISession session, MappingConfiguration config, string tableName, st
public Table(ISession session, MappingConfiguration config, string tableName)
: this(session, config, tableName, null)
{
-
}
///
@@ -106,7 +105,6 @@ public Table(ISession session, MappingConfiguration config, string tableName)
public Table(ISession session, MappingConfiguration config)
: this(session, config, null, null)
{
-
}
///
@@ -146,7 +144,7 @@ object IQueryProvider.Execute(Expression expression)
public Type GetEntityType()
{
- return typeof (TEntity);
+ return typeof(TEntity);
}
public void Create()
@@ -168,8 +166,9 @@ public void CreateIfNotExists()
public async Task CreateAsync()
{
- var metadata = await _session.Cluster.GetMetadataAsync().ConfigureAwait(false);
- var serializer = metadata.SerializerManager.GetCurrentSerializer();
+ // ensure that session is connected before retrieving the current serializer (depends on negotiated protocol version)
+ await _session.ConnectAsync().ConfigureAwait(false);
+ var serializer = _session.Cluster.Configuration.SerializerManager.GetCurrentSerializer();
var cqlQueries = CqlGenerator.GetCreate(serializer, PocoData, Name, KeyspaceName, false);
foreach (var cql in cqlQueries)
{
@@ -214,12 +213,12 @@ public CqlInsert Insert(TEntity entity)
///
/// The entity to insert
///
- /// Determines if the query must be generated using NULL values for null
- /// entity members.
+ /// Determines if the query must be generated using NULL values for null
+ /// entity members.
///
/// Use false if you don't want to consider null values for the INSERT
/// operation (recommended).
- ///
+ ///
///
/// Use true if you want to override all the values in the table,
/// generating tombstones for null values.
@@ -230,4 +229,4 @@ public CqlInsert Insert(TEntity entity, bool insertNulls)
return new CqlInsert(entity, insertNulls, this, StatementFactory, MapperFactory);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Cassandra/DataStax/Graph/GraphOptions.cs b/src/Cassandra/DataStax/Graph/GraphOptions.cs
index a73e7ed1a..78b62f961 100644
--- a/src/Cassandra/DataStax/Graph/GraphOptions.cs
+++ b/src/Cassandra/DataStax/Graph/GraphOptions.cs
@@ -343,7 +343,7 @@ private static byte[] ToUtf8Buffer(string value)
private static byte[] ToBuffer(long value)
{
var serializer = Serialization.TypeSerializer.PrimitiveLongSerializer;
- return serializer.Serialize((ushort) Cluster.MaxProtocolVersion, value);
+ return serializer.Serialize((ushort) Configuration.MaxProtocolVersion, value);
}
}
}
diff --git a/src/Cassandra/DataStax/Insights/IInsightsClient.cs b/src/Cassandra/DataStax/Insights/IInsightsClient.cs
index 3fab45016..6d1ef6655 100644
--- a/src/Cassandra/DataStax/Insights/IInsightsClient.cs
+++ b/src/Cassandra/DataStax/Insights/IInsightsClient.cs
@@ -16,12 +16,13 @@
using System;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
namespace Cassandra.DataStax.Insights
{
internal interface IInsightsClient : IDisposable
{
- Task InitializeAsync();
+ void Initialize(IInternalMetadata internalMetadata);
Task ShutdownAsync();
}
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/IInsightsInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/IInsightsInfoProvider.cs
index 08a9712d2..e33ff0073 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/IInsightsInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/IInsightsInfoProvider.cs
@@ -14,12 +14,13 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra.DataStax.Insights.InfoProviders
{
internal interface IInsightsInfoProvider
{
- T GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata);
+ T GetInformation(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/AuthProviderInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/AuthProviderInfoProvider.cs
index 539d9ab42..4654a952e 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/AuthProviderInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/AuthProviderInfoProvider.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
using Cassandra.SessionManagement;
@@ -22,7 +23,7 @@ namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
internal class AuthProviderInfoProvider : IInsightsInfoProvider
{
public AuthProviderInfo GetInformation(
- IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var type = cluster.Configuration.AuthProvider.GetType();
return new AuthProviderInfo
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ConfigAntiPatternsInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ConfigAntiPatternsInfoProvider.cs
index 648ecd5da..7c80f1622 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ConfigAntiPatternsInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ConfigAntiPatternsInfoProvider.cs
@@ -12,11 +12,13 @@
// 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.
-//
+//
using System;
using System.Collections.Generic;
using System.Linq;
+
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
@@ -27,7 +29,7 @@ static ConfigAntiPatternsInfoProvider()
{
ConfigAntiPatternsInfoProvider.AntiPatternsProviders = new Dictionary, Dictionary>>
{
- {
+ {
#pragma warning disable 618
typeof(DowngradingConsistencyRetryPolicy),
#pragma warning restore 618
@@ -37,16 +39,16 @@ static ConfigAntiPatternsInfoProvider()
return antiPatterns;
}
},
- {
- typeof(DefaultLoadBalancingPolicy),
+ {
+ typeof(DefaultLoadBalancingPolicy),
(obj, antiPatterns) =>
{
var typedPolicy = (DefaultLoadBalancingPolicy) obj;
return ConfigAntiPatternsInfoProvider.AddAntiPatterns(typedPolicy.ChildPolicy, antiPatterns);
}
},
- {
- typeof(RetryLoadBalancingPolicy),
+ {
+ typeof(RetryLoadBalancingPolicy),
(obj, antiPatterns) =>
{
var typedPolicy = (RetryLoadBalancingPolicy) obj;
@@ -54,8 +56,8 @@ static ConfigAntiPatternsInfoProvider()
return ConfigAntiPatternsInfoProvider.AddAntiPatterns(typedPolicy.LoadBalancingPolicy, antiPatterns);
}
},
- {
- typeof(TokenAwarePolicy),
+ {
+ typeof(TokenAwarePolicy),
(obj, antiPatterns) =>
{
var typedPolicy = (TokenAwarePolicy) obj;
@@ -92,11 +94,12 @@ static ConfigAntiPatternsInfoProvider()
public static IReadOnlyDictionary, Dictionary>> AntiPatternsProviders { get; }
- public Dictionary GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public Dictionary GetInformation(
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var antiPatterns = new Dictionary();
- var resolvedContactPoints = metadata.ResolvedContactPoints;
+ var resolvedContactPoints = internalMetadata.ResolvedContactPoints;
var contactPointsEndPoints = resolvedContactPoints
.Values
@@ -104,9 +107,9 @@ public Dictionary GetInformation(IInternalCluster cluster, IInte
.Select(c => c.GetHostIpEndPointWithFallback())
.ToList();
- var contactPointsHosts = metadata
+ var contactPointsHosts = internalMetadata
.AllHosts()
- .Where(host => (host.ContactPoint != null && resolvedContactPoints.ContainsKey(host.ContactPoint))
+ .Where(host => (host.ContactPoint != null && resolvedContactPoints.ContainsKey(host.ContactPoint))
|| contactPointsEndPoints.Contains(host.Address))
.ToList();
@@ -126,8 +129,8 @@ public Dictionary GetInformation(IInternalCluster cluster, IInte
private static Dictionary AddAntiPatterns(object obj, Dictionary antiPatterns)
{
- return ConfigAntiPatternsInfoProvider.AntiPatternsProviders.TryGetValue(obj.GetType(), out var provider)
- ? provider.Invoke(obj, antiPatterns)
+ return ConfigAntiPatternsInfoProvider.AntiPatternsProviders.TryGetValue(obj.GetType(), out var provider)
+ ? provider.Invoke(obj, antiPatterns)
: antiPatterns;
}
}
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs
index ea0e1c6bf..c612e94a4 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs
@@ -12,25 +12,27 @@
// 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.
-//
+//
using System.Collections.Generic;
+
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
{
internal class DataCentersInfoProvider : IInsightsInfoProvider>
{
- public HashSet GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public HashSet GetInformation(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var dataCenters = new HashSet();
var remoteConnectionsLength =
cluster
.Configuration
- .GetOrCreatePoolingOptions(metadata.ProtocolVersion)
+ .GetOrCreatePoolingOptions(internalMetadata.ProtocolVersion)
.GetCoreConnectionsPerHost(HostDistance.Remote);
- foreach (var h in metadata.AllHosts())
+ foreach (var h in internalMetadata.AllHosts())
{
if (h.Datacenter == null)
{
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DriverInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DriverInfoProvider.cs
index f31e083ae..4f6e392a2 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DriverInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DriverInfoProvider.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
using Cassandra.Helpers;
using Cassandra.SessionManagement;
@@ -22,7 +23,7 @@ namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
internal class DriverInfoProvider : IInsightsInfoProvider
{
public DriverInfo GetInformation(
- IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
return new DriverInfo
{
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ExecutionProfileInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ExecutionProfileInfoProvider.cs
index e009fc0a8..186fc00f3 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ExecutionProfileInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ExecutionProfileInfoProvider.cs
@@ -16,6 +16,8 @@
using System.Collections.Generic;
using System.Linq;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema.Converters;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
using Cassandra.SessionManagement;
@@ -37,9 +39,9 @@ public ExecutionProfileInfoProvider(
_speculativeExecutionPolicyInfoProvider = speculativeExecutionPolicyInfoProvider;
_retryPolicyInfoProvider = retryPolicyInfoProvider;
}
-
+
public Dictionary GetInformation(
- IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
// add default first so that it is on top
var dict = new Dictionary
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/HostnameInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/HostnameInfoProvider.cs
index cbe5a0c7c..0b1933423 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/HostnameInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/HostnameInfoProvider.cs
@@ -12,16 +12,18 @@
// 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.
-//
+//
using System.Net;
+
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
{
internal class HostnameInfoProvider : IInsightsInfoProvider
{
- public string GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public string GetInformation(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
return Dns.GetHostName();
}
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/OtherOptionsInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/OtherOptionsInfoProvider.cs
index a157338f6..732b80448 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/OtherOptionsInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/OtherOptionsInfoProvider.cs
@@ -12,16 +12,19 @@
// 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.
-//
+//
using System.Collections.Generic;
+
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
{
internal class OtherOptionsInfoProvider : IInsightsInfoProvider>
{
- public Dictionary GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public Dictionary GetInformation(
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
return null;
}
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PlatformInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PlatformInfoProvider.cs
index 4068a55ee..01015ed12 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PlatformInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PlatformInfoProvider.cs
@@ -18,6 +18,8 @@
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
using Cassandra.Helpers;
using Cassandra.SessionManagement;
@@ -26,7 +28,8 @@ namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
{
internal class PlatformInfoProvider : IInsightsInfoProvider
{
- public InsightsPlatformInfo GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public InsightsPlatformInfo GetInformation(
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var cpuInfo = PlatformHelper.GetCpuInfo();
var dependencies = typeof(PlatformInfoProvider).GetTypeInfo().Assembly.GetReferencedAssemblies().Select(name =>
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PoolSizeByHostDistanceInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PoolSizeByHostDistanceInfoProvider.cs
index 64f7c4a3d..3c6bdc1cf 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PoolSizeByHostDistanceInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/PoolSizeByHostDistanceInfoProvider.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
using Cassandra.SessionManagement;
@@ -21,17 +22,18 @@ namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
{
internal class PoolSizeByHostDistanceInfoProvider : IInsightsInfoProvider
{
- public PoolSizeByHostDistance GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public PoolSizeByHostDistance GetInformation(
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
return new PoolSizeByHostDistance
{
Local = cluster
.Configuration
- .GetOrCreatePoolingOptions(metadata.ProtocolVersion)
+ .GetOrCreatePoolingOptions(internalMetadata.ProtocolVersion)
.GetCoreConnectionsPerHost(HostDistance.Local),
Remote = cluster
.Configuration
- .GetOrCreatePoolingOptions(metadata.ProtocolVersion)
+ .GetOrCreatePoolingOptions(internalMetadata.ProtocolVersion)
.GetCoreConnectionsPerHost(HostDistance.Remote)
};
}
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ReconnectionPolicyInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ReconnectionPolicyInfoProvider.cs
index 1822e358d..7d589c90a 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ReconnectionPolicyInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/ReconnectionPolicyInfoProvider.cs
@@ -12,10 +12,12 @@
// 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.
-//
+//
using System;
using System.Collections.Generic;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
using Cassandra.SessionManagement;
@@ -27,24 +29,24 @@ static ReconnectionPolicyInfoProvider()
{
ReconnectionPolicyInfoProvider.PolicyOptionsProviders = new Dictionary>>
{
- {
- typeof(ConstantReconnectionPolicy),
+ {
+ typeof(ConstantReconnectionPolicy),
policy =>
{
var typedPolicy = (ConstantReconnectionPolicy) policy;
return new Dictionary {{ "constantDelayMs", typedPolicy.ConstantDelayMs }};
}
},
- {
- typeof(ExponentialReconnectionPolicy),
+ {
+ typeof(ExponentialReconnectionPolicy),
policy =>
{
var typedPolicy = (ExponentialReconnectionPolicy) policy;
return new Dictionary {{ "baseDelayMs", typedPolicy.BaseDelayMs }, { "maxDelayMs", typedPolicy.MaxDelayMs }};
}
},
- {
- typeof(FixedReconnectionPolicy),
+ {
+ typeof(FixedReconnectionPolicy),
policy =>
{
var typedPolicy = (FixedReconnectionPolicy) policy;
@@ -56,7 +58,8 @@ static ReconnectionPolicyInfoProvider()
public static IReadOnlyDictionary>> PolicyOptionsProviders { get; }
- public PolicyInfo GetInformation(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public PolicyInfo GetInformation(
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var policy = cluster.Configuration.Policies.ReconnectionPolicy;
var type = policy.GetType();
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StatusMessage/NodeStatusInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StatusMessage/NodeStatusInfoProvider.cs
index ed33a1113..c87024fce 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StatusMessage/NodeStatusInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StatusMessage/NodeStatusInfoProvider.cs
@@ -15,6 +15,8 @@
//
using System.Collections.Generic;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema.StatusMessage;
using Cassandra.SessionManagement;
@@ -23,10 +25,10 @@ namespace Cassandra.DataStax.Insights.InfoProviders.StatusMessage
internal class NodeStatusInfoProvider : IInsightsInfoProvider>
{
public Dictionary GetInformation(
- IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var nodeStatusDictionary = new Dictionary();
- var state = session.GetState(metadata);
+ var state = session.GetState(internalMetadata);
var connectedHosts = state.GetConnectedHosts();
foreach (var h in connectedHosts)
diff --git a/src/Cassandra/DataStax/Insights/InsightsClient.cs b/src/Cassandra/DataStax/Insights/InsightsClient.cs
index a170d9e96..a44bcb5a5 100644
--- a/src/Cassandra/DataStax/Insights/InsightsClient.cs
+++ b/src/Cassandra/DataStax/Insights/InsightsClient.cs
@@ -17,12 +17,15 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.MessageFactories;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
using Cassandra.DataStax.Insights.Schema.StatusMessage;
using Cassandra.Responses;
using Cassandra.SessionManagement;
using Cassandra.Tasks;
+
using Newtonsoft.Json;
namespace Cassandra.DataStax.Insights
@@ -59,19 +62,19 @@ public InsightsClient(
private bool Initialized => _insightsTask != null;
- private async Task SendStartupMessageAsync(Metadata metadata)
+ private async Task SendStartupMessageAsync(IInternalMetadata internalMetadata)
{
try
{
await SendJsonMessageAsync(
- _startupMessageFactory.CreateMessage(_cluster, _session, metadata),
- metadata).ConfigureAwait(false);
+ _startupMessageFactory.CreateMessage(_cluster, _session, internalMetadata),
+ internalMetadata).ConfigureAwait(false);
_errorCount = 0;
return true;
}
catch (Exception ex)
{
- if (_cancellationTokenSource.IsCancellationRequested ||
+ if (_cancellationTokenSource.IsCancellationRequested ||
_errorCount >= InsightsClient.ErrorCountThresholdForLogging)
{
return false;
@@ -83,19 +86,19 @@ await SendJsonMessageAsync(
}
}
- private async Task SendStatusMessageAsync(Metadata metadata)
+ private async Task SendStatusMessageAsync(IInternalMetadata internalMetadata)
{
try
{
await SendJsonMessageAsync(
- _statusMessageFactory.CreateMessage(_cluster, _session, metadata),
- metadata).ConfigureAwait(false);
+ _statusMessageFactory.CreateMessage(_cluster, _session, internalMetadata),
+ internalMetadata).ConfigureAwait(false);
_errorCount = 0;
return true;
}
catch (Exception ex)
{
- if (_cancellationTokenSource.IsCancellationRequested ||
+ if (_cancellationTokenSource.IsCancellationRequested ||
_errorCount >= InsightsClient.ErrorCountThresholdForLogging)
{
return false;
@@ -107,17 +110,16 @@ await SendJsonMessageAsync(
}
}
- public async Task InitializeAsync()
+ public void Initialize(IInternalMetadata internalMetadata)
{
- var metadata = await _cluster.GetMetadataAsync().ConfigureAwait(false);
- if (!ShouldStartInsightsTask(metadata))
+ if (!ShouldStartInsightsTask(internalMetadata))
{
_insightsTask = null;
return;
}
_cancellationTokenSource = new CancellationTokenSource();
- _insightsTask = Task.Run(() => MainLoopAsync(metadata));
+ _insightsTask = Task.Run(() => MainLoopAsync(internalMetadata));
}
public Task ShutdownAsync()
@@ -136,13 +138,13 @@ public void Dispose()
ShutdownAsync().GetAwaiter().GetResult();
}
- private bool ShouldStartInsightsTask(Metadata metadata)
+ private bool ShouldStartInsightsTask(IInternalMetadata internalMetadata)
{
- return _monitorReportingOptions.MonitorReportingEnabled
- && _cluster.Configuration.InsightsSupportVerifier.SupportsInsights(metadata);
+ return _monitorReportingOptions.MonitorReportingEnabled
+ && _cluster.Configuration.InsightsSupportVerifier.SupportsInsights(internalMetadata);
}
- private async Task MainLoopAsync(Metadata metadata)
+ private async Task MainLoopAsync(IInternalMetadata metadata)
{
try
{
@@ -152,9 +154,9 @@ private async Task MainLoopAsync(Metadata metadata)
// The initial delay should contain some random portion
// Initial delay should be statusEventDelay - (0 to 10%)
var percentageToSubtract = new Random(Guid.NewGuid().GetHashCode()).NextDouble() * 0.1;
- var delay = _monitorReportingOptions.StatusEventDelayMilliseconds -
+ var delay = _monitorReportingOptions.StatusEventDelayMilliseconds -
(_monitorReportingOptions.StatusEventDelayMilliseconds * percentageToSubtract);
-
+
while (!_cancellationTokenSource.IsCancellationRequested)
{
if (!startupSent)
@@ -165,7 +167,7 @@ private async Task MainLoopAsync(Metadata metadata)
{
await SendStatusMessageAsync(metadata).ConfigureAwait(false);
}
-
+
await TaskHelper.DelayWithCancellation(
TimeSpan.FromMilliseconds(delay), _cancellationTokenSource.Token).ConfigureAwait(false);
@@ -184,7 +186,7 @@ await TaskHelper.DelayWithCancellation(
InsightsClient.Logger.Verbose("Insights task is ending.");
}
- private async Task SendJsonMessageAsync(T message, Metadata metadata)
+ private async Task SendJsonMessageAsync(T message, IInternalMetadata internalMetadata)
{
var queryProtocolOptions = new QueryProtocolOptions(
ConsistencyLevel.One,
@@ -194,9 +196,9 @@ private async Task SendJsonMessageAsync(T message, Metadata metadata)
null,
ConsistencyLevel.Any);
- var response = await RunWithTokenAsync(() =>
- metadata.ControlConnection.UnsafeSendQueryRequestAsync(
- InsightsClient.ReportInsightRpc,
+ var response = await RunWithTokenAsync(() =>
+ internalMetadata.ControlConnection.UnsafeSendQueryRequestAsync(
+ InsightsClient.ReportInsightRpc,
queryProtocolOptions)).ConfigureAwait(false);
if (response == null)
diff --git a/src/Cassandra/DataStax/Insights/MessageFactories/IInsightsMessageFactory.cs b/src/Cassandra/DataStax/Insights/MessageFactories/IInsightsMessageFactory.cs
index b81d3d596..45c98e5fc 100644
--- a/src/Cassandra/DataStax/Insights/MessageFactories/IInsightsMessageFactory.cs
+++ b/src/Cassandra/DataStax/Insights/MessageFactories/IInsightsMessageFactory.cs
@@ -12,8 +12,9 @@
// 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.
-//
+//
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.Schema;
using Cassandra.SessionManagement;
@@ -21,6 +22,6 @@ namespace Cassandra.DataStax.Insights.MessageFactories
{
internal interface IInsightsMessageFactory
{
- Insight CreateMessage(IInternalCluster cluster, IInternalSession session, Metadata metadata);
+ Insight CreateMessage(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStartupMessageFactory.cs b/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStartupMessageFactory.cs
index 6cafcbd6b..13bec92f0 100644
--- a/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStartupMessageFactory.cs
+++ b/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStartupMessageFactory.cs
@@ -15,6 +15,8 @@
//
using System.Linq;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.InfoProviders;
using Cassandra.DataStax.Insights.Schema;
using Cassandra.DataStax.Insights.Schema.StartupMessage;
@@ -26,7 +28,7 @@ internal class InsightsStartupMessageFactory : IInsightsMessageFactory CreateMessage(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public Insight CreateMessage(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var insightsMetadata = _metadataFactory.CreateInsightsMetadata(
InsightsStartupMessageFactory.StartupMessageName, InsightsStartupMessageFactory.StartupV1MappingId, InsightType.Event);
- var driverInfo = _infoProviders.DriverInfoProvider.GetInformation(cluster, session, metadata);
+ var driverInfo = _infoProviders.DriverInfoProvider.GetInformation(cluster, session, internalMetadata);
var startupData = new InsightsStartupData
{
ClientId = cluster.Configuration.ClusterId.ToString(),
@@ -51,28 +53,28 @@ public Insight CreateMessage(IInternalCluster cluster, IInt
ApplicationName = cluster.Configuration.ApplicationName,
ApplicationVersion = cluster.Configuration.ApplicationVersion,
ApplicationNameWasGenerated = cluster.Configuration.ApplicationNameWasGenerated,
- ContactPoints =
- metadata.ResolvedContactPoints.ToDictionary(
+ ContactPoints =
+ internalMetadata.ResolvedContactPoints.ToDictionary(
kvp => kvp.Key.StringRepresentation, kvp => kvp.Value.Select(ipEndPoint => ipEndPoint.GetHostIpEndPointWithFallback().ToString()).ToList()),
- DataCenters = _infoProviders.DataCentersInfoProvider.GetInformation(cluster, session, metadata),
- InitialControlConnection = metadata.ControlConnection.EndPoint?.GetHostIpEndPointWithFallback().ToString(),
- LocalAddress = metadata.ControlConnection.LocalAddress?.ToString(),
- HostName = _infoProviders.HostnameProvider.GetInformation(cluster, session, metadata),
- ProtocolVersion = (byte)metadata.ProtocolVersion,
- ExecutionProfiles = _infoProviders.ExecutionProfileInfoProvider.GetInformation(cluster, session, metadata),
- PoolSizeByHostDistance = _infoProviders.PoolSizeByHostDistanceInfoProvider.GetInformation(cluster, session, metadata),
- HeartbeatInterval =
+ DataCenters = _infoProviders.DataCentersInfoProvider.GetInformation(cluster, session, internalMetadata),
+ InitialControlConnection = internalMetadata.ControlConnection.EndPoint?.GetHostIpEndPointWithFallback().ToString(),
+ LocalAddress = internalMetadata.ControlConnection.LocalAddress?.ToString(),
+ HostName = _infoProviders.HostnameProvider.GetInformation(cluster, session, internalMetadata),
+ ProtocolVersion = (byte)internalMetadata.ProtocolVersion,
+ ExecutionProfiles = _infoProviders.ExecutionProfileInfoProvider.GetInformation(cluster, session, internalMetadata),
+ PoolSizeByHostDistance = _infoProviders.PoolSizeByHostDistanceInfoProvider.GetInformation(cluster, session, internalMetadata),
+ HeartbeatInterval =
cluster
.Configuration
- .GetOrCreatePoolingOptions(metadata.ProtocolVersion)
+ .GetOrCreatePoolingOptions(internalMetadata.ProtocolVersion)
.GetHeartBeatInterval() ?? 0,
Compression = cluster.Configuration.ProtocolOptions.Compression,
- ReconnectionPolicy = _infoProviders.ReconnectionPolicyInfoProvider.GetInformation(cluster, session, metadata),
+ ReconnectionPolicy = _infoProviders.ReconnectionPolicyInfoProvider.GetInformation(cluster, session, internalMetadata),
Ssl = new SslInfo { Enabled = cluster.Configuration.ProtocolOptions.SslOptions != null },
- AuthProvider = _infoProviders.AuthProviderInfoProvider.GetInformation(cluster, session, metadata),
- OtherOptions = _infoProviders.OtherOptionsInfoProvider.GetInformation(cluster, session, metadata),
- PlatformInfo = _infoProviders.PlatformInfoProvider.GetInformation(cluster, session, metadata),
- ConfigAntiPatterns = _infoProviders.ConfigAntiPatternsInfoProvider.GetInformation(cluster, session, metadata),
+ AuthProvider = _infoProviders.AuthProviderInfoProvider.GetInformation(cluster, session, internalMetadata),
+ OtherOptions = _infoProviders.OtherOptionsInfoProvider.GetInformation(cluster, session, internalMetadata),
+ PlatformInfo = _infoProviders.PlatformInfoProvider.GetInformation(cluster, session, internalMetadata),
+ ConfigAntiPatterns = _infoProviders.ConfigAntiPatternsInfoProvider.GetInformation(cluster, session, internalMetadata),
PeriodicStatusInterval = cluster.Configuration.MonitorReportingOptions.StatusEventDelayMilliseconds / 1000
};
diff --git a/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStatusMessageFactory.cs b/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStatusMessageFactory.cs
index c52272624..98482774b 100644
--- a/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStatusMessageFactory.cs
+++ b/src/Cassandra/DataStax/Insights/MessageFactories/InsightsStatusMessageFactory.cs
@@ -15,6 +15,8 @@
//
using System.Collections.Generic;
+
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights.InfoProviders;
using Cassandra.DataStax.Insights.Schema;
using Cassandra.DataStax.Insights.Schema.StatusMessage;
@@ -38,7 +40,7 @@ public InsightsStatusMessageFactory(
_connectedNodesInfoProvider = connectedNodesInfoProvider;
}
- public Insight CreateMessage(IInternalCluster cluster, IInternalSession session, Metadata metadata)
+ public Insight CreateMessage(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var insightsMetadata = _insightsMetadataFactory.CreateInsightsMetadata(
InsightsStatusMessageFactory.StatusMessageName, InsightsStatusMessageFactory.StatusV1MappingId, InsightType.Event);
@@ -47,8 +49,8 @@ public Insight CreateMessage(IInternalCluster cluster, IInte
{
ClientId = cluster.Configuration.ClusterId.ToString(),
SessionId = session.InternalSessionId.ToString(),
- ControlConnection = metadata.ControlConnection.EndPoint?.GetHostIpEndPointWithFallback().ToString(),
- ConnectedNodes = _connectedNodesInfoProvider.GetInformation(cluster, session, metadata)
+ ControlConnection = internalMetadata.ControlConnection.EndPoint?.GetHostIpEndPointWithFallback().ToString(),
+ ConnectedNodes = _connectedNodesInfoProvider.GetInformation(cluster, session, internalMetadata)
};
return new Insight
diff --git a/src/Cassandra/Extensions.cs b/src/Cassandra/Extensions.cs
index 29a204fd4..83686d239 100644
--- a/src/Cassandra/Extensions.cs
+++ b/src/Cassandra/Extensions.cs
@@ -15,6 +15,7 @@
//
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
using Cassandra.Tasks;
@@ -62,13 +63,19 @@ public static ISessionState GetState(this ISession instance)
public static async Task GetStateAsync(this ISession instance)
{
var session = instance as IInternalSession;
- var metadata = await instance.Cluster.GetMetadataAsync().ConfigureAwait(false);
- return session == null ? SessionState.Empty() : SessionState.From(session, metadata);
+
+ if (session == null)
+ {
+ return SessionState.Empty();
+ }
+
+ var metadata = await session.TryInitAndGetMetadataAsync().ConfigureAwait(false);
+ return SessionState.From(session, metadata);
}
- internal static ISessionState GetState(this IInternalSession instance, Metadata metadata)
+ internal static ISessionState GetState(this IInternalSession instance, IInternalMetadata internalMetadata)
{
- return SessionState.From(instance, metadata);
+ return SessionState.From(instance, internalMetadata);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/ICluster.cs b/src/Cassandra/ICluster.cs
index 392f66998..8014c359c 100644
--- a/src/Cassandra/ICluster.cs
+++ b/src/Cassandra/ICluster.cs
@@ -52,7 +52,7 @@ public interface ICluster : IDisposable
/// It also allows you to subscribe to certain events (e.g. ).
///
///
- Metadata Metadata { get; }
+ IMetadata Metadata { get; }
///
/// Cluster client configuration
diff --git a/src/Cassandra/IMetadata.cs b/src/Cassandra/IMetadata.cs
new file mode 100644
index 000000000..8793a402d
--- /dev/null
+++ b/src/Cassandra/IMetadata.cs
@@ -0,0 +1,274 @@
+//
+// Copyright (C) DataStax Inc.
+//
+// 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.
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Threading.Tasks;
+
+namespace Cassandra
+{
+ ///
+ /// Allows for fetching of metadata about the connected cluster, including known nodes and schema definitions.
+ ///
+ public interface IMetadata
+ {
+ event HostsEventHandler HostsEvent;
+
+ event SchemaChangedEventHandler SchemaChangedEvent;
+
+ ///
+ /// Event that gets triggered when a new host is added to the cluster
+ ///
+ event Action HostAdded;
+
+ ///
+ /// Event that gets triggered when a host has been removed from the cluster
+ ///
+ event Action HostRemoved;
+
+ Configuration Configuration { get; }
+
+ Task GetClusterDescriptionAsync();
+
+ ClusterDescription GetClusterDescription();
+
+ ///
+ ///
+ /// Returns all known hosts of this cluster from the driver's cache. The driver's cache
+ /// is kept up to date using server protocol events so it will not be populated until the initialization is done
+ /// and a connection is open.
+ ///
+ ///
+ /// This method might return an empty collection if the initialization has not finished yet.
+ ///
+ ///
+ ICollection AllHostsSnapshot();
+
+ ///
+ ///
+ /// Get the replicas without performing any I/O (it will use the driver's cache). The driver's cache
+ /// is kept up to date using server protocol events so it will not be populated until the initialization is done
+ /// and a connection is open.
+ ///
+ ///
+ /// This method might return an empty collection if the initialization has not finished yet.
+ ///
+ ///
+ IEnumerable AllReplicasSnapshot();
+
+ ///
+ ///
+ /// Get the replicas for a given keyspace and partition key without performing any I/O (it will use the driver's cache).
+ /// The driver's cache is kept up to date using server protocol events so it will not be populated
+ /// until the initialization is done and a connection is open.
+ ///
+ ///
+ /// This method might return an empty collection if the initialization has not finished yet.
+ ///
+ ///
+ ICollection GetReplicasSnapshot(string keyspaceName, byte[] partitionKey);
+
+ ///
+ ///
+ /// Get the replicas for a partition key without performing any I/O (it will use the driver's cache).
+ /// The driver's cache is kept up to date using server protocol events so it will not be populated
+ /// until the initialization is done and a connection is open.
+ ///
+ ///
+ /// This method might return an empty collection if the initialization has not finished yet.
+ ///
+ ///
+ ICollection GetReplicasSnapshot(byte[] partitionKey);
+
+ Host GetHost(IPEndPoint address);
+
+ Task GetHostAsync(IPEndPoint address);
+
+ ///
+ /// Returns all known hosts of this cluster.
+ ///
+ /// collection of all known hosts of this cluster.
+ ICollection AllHosts();
+
+ ///
+ /// Returns all known hosts of this cluster.
+ ///
+ /// collection of all known hosts of this cluster.
+ Task> AllHostsAsync();
+
+ IEnumerable AllReplicas();
+
+ Task> AllReplicasAsync();
+
+ ///
+ /// Get the replicas for a given partition key and keyspace
+ ///
+ ICollection GetReplicas(string keyspaceName, byte[] partitionKey);
+
+ ///
+ /// Get the replicas for a given partition key
+ ///
+ ICollection GetReplicas(byte[] partitionKey);
+
+ ///
+ /// Get the replicas for a given partition key and keyspace
+ ///
+ Task> GetReplicasAsync(string keyspaceName, byte[] partitionKey);
+
+ ///
+ /// Get the replicas for a given partition key
+ ///
+ Task> GetReplicasAsync(byte[] partitionKey);
+
+ ///
+ /// Returns metadata of specified keyspace.
+ ///
+ /// the name of the keyspace for which metadata should be
+ /// returned.
+ /// the metadata of the requested keyspace or null if
+ /// * keyspace is not a known keyspace.
+ KeyspaceMetadata GetKeyspace(string keyspace);
+
+ ///
+ /// Returns metadata of specified keyspace.
+ ///
+ /// the name of the keyspace for which metadata should be
+ /// returned.
+ /// the metadata of the requested keyspace or null if
+ /// * keyspace is not a known keyspace.
+ Task GetKeyspaceAsync(string keyspace);
+
+ ///
+ /// Returns a collection of all defined keyspaces names.
+ ///
+ /// a collection of all defined keyspaces names.
+ ICollection GetKeyspaces();
+
+ ///
+ /// Returns a collection of all defined keyspaces names.
+ ///
+ /// a collection of all defined keyspaces names.
+ Task> GetKeyspacesAsync();
+
+ ///
+ /// Returns names of all tables which are defined within specified keyspace.
+ ///
+ /// the name of the keyspace for which all tables metadata should be
+ /// returned.
+ /// an ICollection of the metadata for the tables defined in this
+ /// keyspace.
+ ICollection GetTables(string keyspace);
+
+ ///
+ /// Returns names of all tables which are defined within specified keyspace.
+ ///
+ /// the name of the keyspace for which all tables metadata should be
+ /// returned.
+ /// an ICollection of the metadata for the tables defined in this
+ /// keyspace.
+ Task> GetTablesAsync(string keyspace);
+
+ ///
+ /// Returns TableMetadata for specified table in specified keyspace.
+ ///
+ /// name of the keyspace within specified table is defined.
+ /// name of table for which metadata should be returned.
+ /// a TableMetadata for the specified table in the specified keyspace.
+ TableMetadata GetTable(string keyspace, string tableName);
+
+ ///
+ /// Returns TableMetadata for specified table in specified keyspace.
+ ///
+ /// name of the keyspace within specified table is defined.
+ /// name of table for which metadata should be returned.
+ /// a TableMetadata for the specified table in the specified keyspace.
+ Task GetTableAsync(string keyspace, string tableName);
+
+ ///
+ /// Returns the view metadata for the provided view name in the keyspace.
+ ///
+ /// name of the keyspace within specified view is defined.
+ /// name of view.
+ /// a MaterializedViewMetadata for the view in the specified keyspace.
+ MaterializedViewMetadata GetMaterializedView(string keyspace, string name);
+
+ ///
+ /// Returns the view metadata for the provided view name in the keyspace.
+ ///
+ /// name of the keyspace within specified view is defined.
+ /// name of view.
+ /// a MaterializedViewMetadata for the view in the specified keyspace.
+ Task GetMaterializedViewAsync(string keyspace, string name);
+
+ ///
+ /// Gets the definition associated with a User Defined Type from Cassandra
+ ///
+ UdtColumnInfo GetUdtDefinition(string keyspace, string typeName);
+
+ ///
+ /// Gets the definition associated with a User Defined Type from Cassandra
+ ///
+ Task GetUdtDefinitionAsync(string keyspace, string typeName);
+
+ ///
+ /// Gets the definition associated with a User Defined Function from Cassandra
+ ///
+ /// The function metadata or null if not found.
+ FunctionMetadata GetFunction(string keyspace, string name, string[] signature);
+
+ ///
+ /// Gets the definition associated with a User Defined Function from Cassandra
+ ///
+ /// The function metadata or null if not found.
+ Task GetFunctionAsync(string keyspace, string name, string[] signature);
+
+ ///
+ /// Gets the definition associated with a aggregate from Cassandra
+ ///
+ /// The aggregate metadata or null if not found.
+ AggregateMetadata GetAggregate(string keyspace, string name, string[] signature);
+
+ ///
+ /// Gets the definition associated with a aggregate from Cassandra
+ ///
+ /// The aggregate metadata or null if not found.
+ Task GetAggregateAsync(string keyspace, string name, string[] signature);
+
+ ///
+ /// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
+ /// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
+ ///
+ bool RefreshSchema(string keyspace = null, string table = null);
+
+ ///
+ /// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
+ /// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
+ ///
+ Task RefreshSchemaAsync(string keyspace = null, string table = null);
+
+ ///
+ /// Initiates a schema agreement check.
+ ///
+ /// Schema changes need to be propagated to all nodes in the cluster.
+ /// Once they have settled on a common version, we say that they are in agreement.
+ ///
+ /// This method does not perform retries so
+ /// does not apply.
+ ///
+ /// True if schema agreement was successful and false if it was not successful.
+ Task CheckSchemaAgreementAsync();
+ }
+}
\ No newline at end of file
diff --git a/src/Cassandra/ISession.cs b/src/Cassandra/ISession.cs
index b53ade1e1..6e9053270 100644
--- a/src/Cassandra/ISession.cs
+++ b/src/Cassandra/ISession.cs
@@ -67,7 +67,9 @@ public interface ISession: IDisposable
///
///
- /// Waits until the initialization task is finished (this task is started when the Session is created).
+ /// Waits until the initialization task is finished (this task is started when the Session is created).
+ /// If the session is already initialized, this method returns without blocking.
+ ///
///
/// It is not necessary to call this method but you can use it if you want the initialization to happen
/// in a specific state of your application (e.g. during startup before your application listens for requests).
@@ -83,8 +85,11 @@ public interface ISession: IDisposable
///
///
- /// Waits until the initialization task is finished (this task is started when the Session is created).
- /// It is not necessary to call this method but you can use it if you want the initialization to happen
+ /// Waits until the initialization task is finished (this task is started when the Session is created).
+ /// If the session is already initialized, this method returns a completed Task.
+ ///
+ ///
+ /// It is not necessary to call this method but you can use it if you want the initialization to happen
/// in a specific state of your application (e.g. during startup before your application listens for requests).
///
///
diff --git a/src/Cassandra/KeyspaceMetadata.cs b/src/Cassandra/KeyspaceMetadata.cs
index 96b8f1117..ce9e84cb1 100644
--- a/src/Cassandra/KeyspaceMetadata.cs
+++ b/src/Cassandra/KeyspaceMetadata.cs
@@ -20,6 +20,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
using Cassandra.MetadataHelpers;
using Cassandra.Tasks;
@@ -31,7 +32,7 @@ public class KeyspaceMetadata
private readonly ConcurrentDictionary _views = new ConcurrentDictionary();
private readonly ConcurrentDictionary, FunctionMetadata> _functions = new ConcurrentDictionary, FunctionMetadata>();
private readonly ConcurrentDictionary, AggregateMetadata> _aggregates = new ConcurrentDictionary, AggregateMetadata>();
- private readonly Metadata _parent;
+ private readonly IInternalMetadata _parent;
///
/// Gets the name of this keyspace.
@@ -73,14 +74,14 @@ public class KeyspaceMetadata
internal IReplicationStrategy Strategy { get; }
- internal KeyspaceMetadata(Metadata parent, string name, bool durableWrites, string strategyClass,
+ internal KeyspaceMetadata(IInternalMetadata parent, string name, bool durableWrites, string strategyClass,
IDictionary replicationOptions, bool isVirtual = false)
: this(parent, name, durableWrites, strategyClass, replicationOptions, new ReplicationStrategyFactory(), isVirtual)
{
}
internal KeyspaceMetadata(
- Metadata parent,
+ IInternalMetadata parent,
string name,
bool durableWrites,
string strategyClass,
diff --git a/src/Cassandra/Metadata.cs b/src/Cassandra/Metadata.cs
index 399215ff5..433b658ad 100644
--- a/src/Cassandra/Metadata.cs
+++ b/src/Cassandra/Metadata.cs
@@ -27,38 +27,21 @@
namespace Cassandra
{
- ///
- /// Keeps metadata on the connected cluster, including known nodes and schema
- /// definitions.
- ///
- public class Metadata
+ ///
+ internal class Metadata : IMetadata
{
private const int Disposed = 10;
private const int Initialized = 5;
private const int Initializing = 1;
private const int NotInitialized = 0;
- private readonly InternalMetadata _internalMetadata;
private readonly int _queryAbortTimeout;
+
private volatile Task _initTask;
private long _state = Metadata.NotInitialized;
- internal InternalMetadata InternalMetadata => _internalMetadata;
-
- public event HostsEventHandler HostsEvent;
-
- public event SchemaChangedEventHandler SchemaChangedEvent;
-
- ///
- /// Event that gets triggered when a new host is added to the cluster
- ///
- public event Action HostAdded;
-
- ///
- /// Event that gets triggered when a host has been removed from the cluster
- ///
- public event Action HostRemoved;
+ internal InternalMetadata InternalMetadata { get; }
internal Metadata(
IInternalCluster cluster,
@@ -67,7 +50,8 @@ internal Metadata(
IEnumerable parsedContactPoints)
{
_queryAbortTimeout = configuration.DefaultRequestOptions.QueryAbortTimeout;
- _internalMetadata = new InternalMetadata(cluster, this, configuration, serializerManager, parsedContactPoints);
+ Configuration = configuration;
+ InternalMetadata = new InternalMetadata(cluster, this, configuration, serializerManager, parsedContactPoints);
}
internal Metadata(
@@ -78,386 +62,347 @@ internal Metadata(
SchemaParser schemaParser)
{
_queryAbortTimeout = configuration.DefaultRequestOptions.QueryAbortTimeout;
- _internalMetadata = new InternalMetadata(
+ Configuration = configuration;
+ InternalMetadata = new InternalMetadata(
cluster, this, configuration, serializerManager, parsedContactPoints, schemaParser);
}
- internal Task TryInitializeAsync()
- {
- var currentState = Interlocked.Read(ref _state);
- if (currentState == Metadata.Initialized)
- {
- //It was already initialized
- return TaskHelper.Completed;
- }
+ public Configuration Configuration { get; }
- return InitializeAsync(currentState);
- }
-
- private Task InitializeAsync(long currentState)
- {
- if (currentState == Metadata.Disposed)
- {
- throw new ObjectDisposedException("This metadata object has been disposed.");
- }
-
- if (Interlocked.CompareExchange(ref _state, Metadata.Initializing, Metadata.NotInitialized)
- == Metadata.NotInitialized)
- {
- _initTask = Task.Run(InitializeInternalAsync);
- }
-
- return _initTask;
- }
-
- private async Task InitializeInternalAsync()
- {
- await _internalMetadata.InitAsync().ConfigureAwait(false);
- var previousState = Interlocked.CompareExchange(ref _state, Metadata.Initialized, Metadata.Initializing);
- if (previousState == Metadata.Disposed)
- {
- await _internalMetadata.ShutdownAsync().ConfigureAwait(false);
- throw new ObjectDisposedException("Metadata instance was disposed before initialization finished.");
- }
- }
+ ///
+ public event HostsEventHandler HostsEvent;
- internal async Task ShutdownAsync()
- {
- var previousState = Interlocked.Exchange(ref _state, Metadata.Disposed);
+ ///
+ public event SchemaChangedEventHandler SchemaChangedEvent;
- if (previousState != Metadata.Initialized)
- {
- return;
- }
+ ///
+ public event Action HostAdded;
- await _internalMetadata.ShutdownAsync().ConfigureAwait(false);
- }
+ ///
+ public event Action HostRemoved;
- internal void OnHostRemoved(Host h)
+ ///
+ public async Task GetClusterDescriptionAsync()
{
- HostRemoved?.Invoke(h);
+ await TryInitializeAsync().ConfigureAwait(false);
+ return GetClusterDescriptionInternal();
}
- internal void OnHostAdded(Host h)
+ ///
+ public ClusterDescription GetClusterDescription()
{
- HostAdded?.Invoke(h);
+ TryInitialize();
+ return GetClusterDescriptionInternal();
}
- public async Task GetClusterDescriptionAsync()
+ ///
+ public ICollection AllHostsSnapshot()
{
- await TryInitializeAsync().ConfigureAwait(false);
- return new ClusterDescription(
- _internalMetadata.ClusterName, _internalMetadata.IsDbaas, _internalMetadata.ProtocolVersion);
+ return InternalMetadata.AllHosts();
}
- public ClusterDescription GetClusterDescription()
+ ///
+ public IEnumerable AllReplicasSnapshot()
{
- return TaskHelper.WaitToComplete(GetClusterDescriptionAsync());
+ return InternalMetadata.AllReplicas();
}
- public Host GetHost(IPEndPoint address)
+ ///
+ public ICollection GetReplicasSnapshot(string keyspaceName, byte[] partitionKey)
{
- return TaskHelper.WaitToComplete(GetHostAsync(address), _queryAbortTimeout);
+ return InternalMetadata.GetReplicas(keyspaceName, partitionKey);
}
- public async Task GetHostAsync(IPEndPoint address)
+ ///
+ public ICollection GetReplicasSnapshot(byte[] partitionKey)
{
- await TryInitializeAsync().ConfigureAwait(false);
- return _internalMetadata.GetHost(address);
+ return GetReplicasSnapshot(null, partitionKey);
}
- internal void FireSchemaChangedEvent(SchemaChangedEventArgs.Kind what, string keyspace, string table, object sender = null)
+ private ClusterDescription GetClusterDescriptionInternal()
{
- SchemaChangedEvent?.Invoke(sender ?? this, new SchemaChangedEventArgs { Keyspace = keyspace, What = what, Table = table });
+ return new ClusterDescription(
+ InternalMetadata.ClusterName, InternalMetadata.IsDbaas, InternalMetadata.ProtocolVersion);
}
- internal void OnHostDown(Host h)
+ ///
+ public Host GetHost(IPEndPoint address)
{
- HostsEvent?.Invoke(this, new HostsEventArgs { Address = h.Address, What = HostsEventArgs.Kind.Down });
+ TryInitialize();
+ return InternalMetadata.GetHost(address);
}
- internal void OnHostUp(Host h)
+ ///
+ public async Task GetHostAsync(IPEndPoint address)
{
- HostsEvent?.Invoke(h, new HostsEventArgs { Address = h.Address, What = HostsEventArgs.Kind.Up });
+ await TryInitializeAsync().ConfigureAwait(false);
+ return InternalMetadata.GetHost(address);
}
- ///
- /// Returns all known hosts of this cluster.
- ///
- /// collection of all known hosts of this cluster.
+ ///
public ICollection AllHosts()
{
- return TaskHelper.WaitToComplete(AllHostsAsync(), _queryAbortTimeout);
+ TryInitialize();
+ return InternalMetadata.AllHosts();
}
- ///
- /// Returns all known hosts of this cluster.
- ///
- /// collection of all known hosts of this cluster.
+ ///
public async Task> AllHostsAsync()
{
await TryInitializeAsync().ConfigureAwait(false);
- return _internalMetadata.AllHosts();
+ return InternalMetadata.AllHosts();
}
+ ///
public IEnumerable AllReplicas()
{
- return TaskHelper.WaitToComplete(AllReplicasAsync(), _queryAbortTimeout);
+ TryInitialize();
+ return InternalMetadata.AllReplicas();
}
+ ///
public async Task> AllReplicasAsync()
{
await TryInitializeAsync().ConfigureAwait(false);
- return _internalMetadata.AllReplicas();
+ return InternalMetadata.AllReplicas();
}
- ///
- /// Get the replicas for a given partition key and keyspace
- ///
+ ///
public ICollection GetReplicas(string keyspaceName, byte[] partitionKey)
{
- return TaskHelper.WaitToComplete(GetReplicasAsync(keyspaceName, partitionKey));
+ TryInitialize();
+ return InternalMetadata.GetReplicas(keyspaceName, partitionKey);
}
- ///
- /// Get the replicas for a given partition key
- ///
+ ///
public ICollection GetReplicas(byte[] partitionKey)
{
return GetReplicas(null, partitionKey);
}
- ///
- /// Get the replicas for a given partition key and keyspace
- ///
+ ///
public async Task> GetReplicasAsync(string keyspaceName, byte[] partitionKey)
{
await TryInitializeAsync().ConfigureAwait(false);
- return _internalMetadata.GetReplicas(keyspaceName, partitionKey);
+ return InternalMetadata.GetReplicas(keyspaceName, partitionKey);
}
- ///
- /// Get the replicas for a given partition key
- ///
+ ///
public Task> GetReplicasAsync(byte[] partitionKey)
{
return GetReplicasAsync(null, partitionKey);
}
- ///
- /// Returns metadata of specified keyspace.
- ///
- /// the name of the keyspace for which metadata should be
- /// returned.
- /// the metadata of the requested keyspace or null if
- /// * keyspace is not a known keyspace.
+ ///
public KeyspaceMetadata GetKeyspace(string keyspace)
{
return TaskHelper.WaitToComplete(GetKeyspaceAsync(keyspace), _queryAbortTimeout);
}
- ///
- /// Returns metadata of specified keyspace.
- ///
- /// the name of the keyspace for which metadata should be
- /// returned.
- /// the metadata of the requested keyspace or null if
- /// * keyspace is not a known keyspace.
+ ///
public async Task GetKeyspaceAsync(string keyspace)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetKeyspaceAsync(keyspace).ConfigureAwait(false);
+ return await InternalMetadata.GetKeyspaceAsync(keyspace).ConfigureAwait(false);
}
- ///
- /// Returns a collection of all defined keyspaces names.
- ///
- /// a collection of all defined keyspaces names.
+ ///
public ICollection GetKeyspaces()
{
return TaskHelper.WaitToComplete(GetKeyspacesAsync(), _queryAbortTimeout);
}
- ///
- /// Returns a collection of all defined keyspaces names.
- ///
- /// a collection of all defined keyspaces names.
+ ///
public async Task> GetKeyspacesAsync()
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetKeyspacesAsync().ConfigureAwait(false);
+ return await InternalMetadata.GetKeyspacesAsync().ConfigureAwait(false);
}
- ///
- /// Returns names of all tables which are defined within specified keyspace.
- ///
- /// the name of the keyspace for which all tables metadata should be
- /// returned.
- /// an ICollection of the metadata for the tables defined in this
- /// keyspace.
+ ///
public ICollection GetTables(string keyspace)
{
return TaskHelper.WaitToComplete(GetTablesAsync(keyspace), _queryAbortTimeout);
}
- ///
- /// Returns names of all tables which are defined within specified keyspace.
- ///
- /// the name of the keyspace for which all tables metadata should be
- /// returned.
- /// an ICollection of the metadata for the tables defined in this
- /// keyspace.
+ ///
public async Task> GetTablesAsync(string keyspace)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetTablesAsync(keyspace).ConfigureAwait(false);
+ return await InternalMetadata.GetTablesAsync(keyspace).ConfigureAwait(false);
}
- ///
- /// Returns TableMetadata for specified table in specified keyspace.
- ///
- /// name of the keyspace within specified table is defined.
- /// name of table for which metadata should be returned.
- /// a TableMetadata for the specified table in the specified keyspace.
+ ///
public TableMetadata GetTable(string keyspace, string tableName)
{
return TaskHelper.WaitToComplete(GetTableAsync(keyspace, tableName), _queryAbortTimeout * 2);
}
- ///
- /// Returns TableMetadata for specified table in specified keyspace.
- ///
- /// name of the keyspace within specified table is defined.
- /// name of table for which metadata should be returned.
- /// a TableMetadata for the specified table in the specified keyspace.
+ ///
public async Task GetTableAsync(string keyspace, string tableName)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetTableAsync(keyspace, tableName).ConfigureAwait(false);
+ return await InternalMetadata.GetTableAsync(keyspace, tableName).ConfigureAwait(false);
}
- ///
- /// Returns the view metadata for the provided view name in the keyspace.
- ///
- /// name of the keyspace within specified view is defined.
- /// name of view.
- /// a MaterializedViewMetadata for the view in the specified keyspace.
+ ///
public MaterializedViewMetadata GetMaterializedView(string keyspace, string name)
{
return TaskHelper.WaitToComplete(GetMaterializedViewAsync(keyspace, name), _queryAbortTimeout * 2);
}
- ///
- /// Returns the view metadata for the provided view name in the keyspace.
- ///
- /// name of the keyspace within specified view is defined.
- /// name of view.
- /// a MaterializedViewMetadata for the view in the specified keyspace.
+ ///
public async Task GetMaterializedViewAsync(string keyspace, string name)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetMaterializedViewAsync(keyspace, name).ConfigureAwait(false);
+ return await InternalMetadata.GetMaterializedViewAsync(keyspace, name).ConfigureAwait(false);
}
-
- ///
- /// Gets the definition associated with a User Defined Type from Cassandra
- ///
+
+ ///
public UdtColumnInfo GetUdtDefinition(string keyspace, string typeName)
{
return TaskHelper.WaitToComplete(GetUdtDefinitionAsync(keyspace, typeName), _queryAbortTimeout);
}
- ///
- /// Gets the definition associated with a User Defined Type from Cassandra
- ///
+ ///
public async Task GetUdtDefinitionAsync(string keyspace, string typeName)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetUdtDefinitionAsync(keyspace, typeName).ConfigureAwait(false);
+ return await InternalMetadata.GetUdtDefinitionAsync(keyspace, typeName).ConfigureAwait(false);
}
- ///
- /// Gets the definition associated with a User Defined Function from Cassandra
- ///
- /// The function metadata or null if not found.
+ ///
public FunctionMetadata GetFunction(string keyspace, string name, string[] signature)
{
return TaskHelper.WaitToComplete(GetFunctionAsync(keyspace, name, signature), _queryAbortTimeout);
}
- ///
- /// Gets the definition associated with a User Defined Function from Cassandra
- ///
- /// The function metadata or null if not found.
+ ///
public async Task GetFunctionAsync(string keyspace, string name, string[] signature)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetFunctionAsync(keyspace, name, signature).ConfigureAwait(false);
+ return await InternalMetadata.GetFunctionAsync(keyspace, name, signature).ConfigureAwait(false);
}
- ///
- /// Gets the definition associated with a aggregate from Cassandra
- ///
- /// The aggregate metadata or null if not found.
+ ///
public AggregateMetadata GetAggregate(string keyspace, string name, string[] signature)
{
return TaskHelper.WaitToComplete(GetAggregateAsync(keyspace, name, signature), _queryAbortTimeout);
}
- ///
- /// Gets the definition associated with a aggregate from Cassandra
- ///
- /// The aggregate metadata or null if not found.
+ ///
public async Task GetAggregateAsync(string keyspace, string name, string[] signature)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetAggregateAsync(keyspace, name, signature).ConfigureAwait(false);
- }
-
- ///
- /// Gets the query trace.
- ///
- /// The query trace that contains the id, which properties are going to be populated.
- ///
- internal async Task GetQueryTraceAsync(QueryTrace trace)
- {
- await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.GetQueryTraceAsync(trace).ConfigureAwait(false);
+ return await InternalMetadata.GetAggregateAsync(keyspace, name, signature).ConfigureAwait(false);
}
- ///
- /// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
- /// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
- ///
+ ///
public bool RefreshSchema(string keyspace = null, string table = null)
{
return TaskHelper.WaitToComplete(RefreshSchemaAsync(keyspace, table), _queryAbortTimeout * 2);
}
- ///
- /// Updates keyspace metadata (including token metadata for token aware routing) for a given keyspace or a specific keyspace table.
- /// If no keyspace is provided then this method will update the metadata and token map for all the keyspaces of the cluster.
- ///
+ ///
public async Task RefreshSchemaAsync(string keyspace = null, string table = null)
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.RefreshSchemaAsync(keyspace, table).ConfigureAwait(false);
- }
-
- ///
- /// Initiates a schema agreement check.
- ///
- /// Schema changes need to be propagated to all nodes in the cluster.
- /// Once they have settled on a common version, we say that they are in agreement.
- ///
- /// This method does not perform retries so
- /// does not apply.
- ///
- /// True if schema agreement was successful and false if it was not successful.
+ return await InternalMetadata.RefreshSchemaAsync(keyspace, table).ConfigureAwait(false);
+ }
+
+ ///
public async Task CheckSchemaAgreementAsync()
{
await TryInitializeAsync().ConfigureAwait(false);
- return await _internalMetadata.CheckSchemaAgreementAsync().ConfigureAwait(false);
+ return await InternalMetadata.CheckSchemaAgreementAsync().ConfigureAwait(false);
+ }
+
+ internal Task TryInitializeAsync()
+ {
+ var currentState = Interlocked.Read(ref _state);
+ if (currentState == Metadata.Initialized)
+ {
+ //It was already initialized
+ return TaskHelper.Completed;
+ }
+
+ return InitializeAsync(currentState);
+ }
+
+ internal void TryInitialize()
+ {
+ var currentState = Interlocked.Read(ref _state);
+ if (currentState == Metadata.Initialized)
+ {
+ //It was already initialized
+ return;
+ }
+
+ TaskHelper.WaitToComplete(InitializeAsync(currentState), _queryAbortTimeout);
+ }
+
+ private Task InitializeAsync(long currentState)
+ {
+ if (currentState == Metadata.Disposed)
+ {
+ throw new ObjectDisposedException("This metadata object has been disposed.");
+ }
+
+ if (Interlocked.CompareExchange(ref _state, Metadata.Initializing, Metadata.NotInitialized)
+ == Metadata.NotInitialized)
+ {
+ _initTask = Task.Run(InitializeInternalAsync);
+ }
+
+ return _initTask;
+ }
+
+ private async Task InitializeInternalAsync()
+ {
+ await InternalMetadata.InitAsync().ConfigureAwait(false);
+ var previousState = Interlocked.CompareExchange(ref _state, Metadata.Initialized, Metadata.Initializing);
+ if (previousState == Metadata.Disposed)
+ {
+ await InternalMetadata.ShutdownAsync().ConfigureAwait(false);
+ throw new ObjectDisposedException("Metadata instance was disposed before initialization finished.");
+ }
+ }
+
+ internal async Task ShutdownAsync()
+ {
+ var previousState = Interlocked.Exchange(ref _state, Metadata.Disposed);
+
+ if (previousState != Metadata.Initialized)
+ {
+ return;
+ }
+
+ await InternalMetadata.ShutdownAsync().ConfigureAwait(false);
+ }
+
+ internal void OnHostRemoved(Host h)
+ {
+ HostRemoved?.Invoke(h);
+ }
+
+ internal void OnHostAdded(Host h)
+ {
+ HostAdded?.Invoke(h);
+ }
+
+ internal void FireSchemaChangedEvent(SchemaChangedEventArgs.Kind what, string keyspace, string table, object sender = null)
+ {
+ SchemaChangedEvent?.Invoke(sender ?? this, new SchemaChangedEventArgs { Keyspace = keyspace, What = what, Table = table });
+ }
+
+ internal void OnHostDown(Host h)
+ {
+ HostsEvent?.Invoke(this, new HostsEventArgs { Address = h.Address, What = HostsEventArgs.Kind.Down });
+ }
+
+ internal void OnHostUp(Host h)
+ {
+ HostsEvent?.Invoke(h, new HostsEventArgs { Address = h.Address, What = HostsEventArgs.Kind.Up });
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs b/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs
index 6848dba10..20ff257b6 100644
--- a/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs
+++ b/src/Cassandra/MetadataHelpers/ISchemaParserFactory.cs
@@ -21,7 +21,7 @@ namespace Cassandra.MetadataHelpers
{
internal interface ISchemaParserFactory
{
- ISchemaParser Create(Version cassandraVersion, InternalMetadata parent,
+ ISchemaParser Create(Version cassandraVersion, IInternalMetadata parent,
Func> udtResolver,
ISchemaParser currentInstance = null);
}
diff --git a/src/Cassandra/MetadataHelpers/SchemaParserFactory.cs b/src/Cassandra/MetadataHelpers/SchemaParserFactory.cs
index bba51c33e..965293b2d 100644
--- a/src/Cassandra/MetadataHelpers/SchemaParserFactory.cs
+++ b/src/Cassandra/MetadataHelpers/SchemaParserFactory.cs
@@ -15,6 +15,7 @@
using System;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
namespace Cassandra.MetadataHelpers
{
@@ -27,7 +28,7 @@ internal class SchemaParserFactory : ISchemaParserFactory
///
/// Creates a new instance if the currentInstance is not valid for the given Cassandra version
///
- public ISchemaParser Create(Version cassandraVersion, Metadata parent,
+ public ISchemaParser Create(Version cassandraVersion, IInternalMetadata parent,
Func> udtResolver,
ISchemaParser currentInstance = null)
{
diff --git a/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
index 03831dea2..c57ee3aae 100644
--- a/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ConstantSpeculativeExecutionPolicy.cs
@@ -51,12 +51,12 @@ public ConstantSpeculativeExecutionPolicy(long delay, int maxSpeculativeExecutio
public int MaxSpeculativeExecutions { get; }
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
return TaskHelper.Completed;
}
- public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
+ public ISpeculativeExecutionPlan NewPlan(IMetadata metadata, string keyspace, IStatement statement)
{
return new ConstantSpeculativeExecutionPlan(Delay, MaxSpeculativeExecutions);
}
diff --git a/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs b/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
index 7a1818b3e..7ee2aed15 100644
--- a/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
@@ -78,7 +78,7 @@ public DCAwareRoundRobinPolicy(string localDc) : this(inferLocalDc: false)
///
public string LocalDc { get; private set; }
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
//When the pool changes, it should clear the local cache
metadata.HostAdded += _ => ClearHosts();
@@ -117,7 +117,7 @@ public HostDistance Distance(Host host)
/// the query for which to build the plan.
/// a new query plan, i.e. an iterator indicating which host to try
/// first for querying, which one to use as failover, etc...
- public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
var startIndex = Interlocked.Increment(ref _index);
@@ -149,7 +149,7 @@ private string GetDatacenter(Host host)
///
/// Gets a tuple containing the list of local and remote nodes
///
- internal List GetHosts(Metadata metadata)
+ internal List GetHosts(IMetadata metadata)
{
var hosts = _hosts;
if (hosts != null)
@@ -167,7 +167,7 @@ internal List GetHosts(Metadata metadata)
}
//shallow copy the nodes
- var allNodes = metadata.AllHosts().ToArray();
+ var allNodes = metadata.AllHostsSnapshot();
hosts = allNodes.Where(h => GetDatacenter(h) == LocalDc).ToList();
_hosts = hosts;
diff --git a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
index e8e01155b..7b4237197 100644
--- a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
@@ -85,7 +85,7 @@ public HostDistance Distance(Host host)
///
/// Initializes the policy.
///
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
return ChildPolicy.InitializeAsync(metadata);
}
@@ -93,7 +93,7 @@ public Task InitializeAsync(Metadata metadata)
///
/// Returns the hosts to used for a query.
///
- public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement statement)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement statement)
{
if (statement is TargettedSimpleStatement targetedStatement && targetedStatement.PreferredHost != null)
{
@@ -104,7 +104,7 @@ public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IState
return ChildPolicy.NewQueryPlan(metadata, keyspace, statement);
}
- private IEnumerable YieldPreferred(Metadata metadata, string keyspace, TargettedSimpleStatement statement)
+ private IEnumerable YieldPreferred(IMetadata metadata, string keyspace, TargettedSimpleStatement statement)
{
yield return statement.PreferredHost;
foreach (var h in ChildPolicy.NewQueryPlan(metadata, keyspace, statement))
diff --git a/src/Cassandra/Policies/ILoadBalancingPolicy.cs b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
index 00210c81c..dca459aba 100644
--- a/src/Cassandra/Policies/ILoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
@@ -39,7 +39,7 @@ public interface ILoadBalancingPolicy
///
///
/// The information about the session instance for which the policy is created.
- Task InitializeAsync(Metadata metadata);
+ Task InitializeAsync(IMetadata metadata);
///
/// Returns the distance assigned by this policy to the provided host.
The
@@ -70,6 +70,6 @@ public interface ILoadBalancingPolicy
/// An iterator of Host. The query is tried against the hosts returned
/// by this iterator in order, until the query has been sent successfully to one
/// of the host.
- IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query);
+ IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query);
}
}
diff --git a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
index 065c47890..9d280793c 100644
--- a/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/ISpeculativeExecutionPolicy.cs
@@ -28,15 +28,16 @@ public interface ISpeculativeExecutionPolicy
///
/// Initializes the policy at cluster startup.
///
- Task InitializeAsync(Metadata metadata);
+ Task InitializeAsync(IMetadata metadata);
///
/// Returns the plan to use for a new query.
///
+ /// the metadata instance for the current session.
/// the currently logged keyspace
/// the query for which to build a plan.
///
- ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement);
+ ISpeculativeExecutionPlan NewPlan(IMetadata metadata, string keyspace, IStatement statement);
///
/// Disposes the policy at cluster shutdown.
diff --git a/src/Cassandra/Policies/LocalDatacenterProvider.cs b/src/Cassandra/Policies/LocalDatacenterProvider.cs
index 19adbe7eb..a1e2b9a2d 100644
--- a/src/Cassandra/Policies/LocalDatacenterProvider.cs
+++ b/src/Cassandra/Policies/LocalDatacenterProvider.cs
@@ -26,12 +26,12 @@ internal class LocalDatacenterProvider : ILocalDatacenterProvider
private volatile bool _initialized = false;
private volatile IInternalCluster _cluster;
- private volatile InternalMetadata _internalMetadata;
+ private volatile IInternalMetadata _internalMetadata;
private volatile IEnumerable _availableDcs;
private volatile string _availableDcsStr;
private volatile string _cachedDatacenter;
- public void Initialize(IInternalCluster cluster, InternalMetadata internalMetadata)
+ public void Initialize(IInternalCluster cluster, IInternalMetadata internalMetadata)
{
_cluster = cluster;
_internalMetadata = internalMetadata;
diff --git a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
index 3717ee358..9adfd630a 100644
--- a/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
+++ b/src/Cassandra/Policies/NoSpeculativeExecutionPolicy.cs
@@ -33,12 +33,12 @@ private NoSpeculativeExecutionPolicy()
}
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
return TaskHelper.Completed;
}
- public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
+ public ISpeculativeExecutionPlan NewPlan(IMetadata metadata, string keyspace, IStatement statement)
{
return Plan;
}
diff --git a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
index a89bdc77c..8bfc38c93 100644
--- a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
@@ -35,7 +35,7 @@ public RetryLoadBalancingPolicy(ILoadBalancingPolicy loadBalancingPolicy, IRecon
public ILoadBalancingPolicy LoadBalancingPolicy { get; }
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
return LoadBalancingPolicy.InitializeAsync(metadata);
}
@@ -45,7 +45,7 @@ public HostDistance Distance(Host host)
return LoadBalancingPolicy.Distance(host);
}
- public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
IReconnectionSchedule schedule = ReconnectionPolicy.NewSchedule();
while (true)
diff --git a/src/Cassandra/Policies/RoundRobinPolicy.cs b/src/Cassandra/Policies/RoundRobinPolicy.cs
index 422814ee9..0663349dd 100644
--- a/src/Cassandra/Policies/RoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/RoundRobinPolicy.cs
@@ -38,7 +38,7 @@ public class RoundRobinPolicy : ILoadBalancingPolicy
{
private int _index;
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
return TaskHelper.Completed;
}
@@ -67,10 +67,10 @@ public HostDistance Distance(Host host)
/// the query for which to build the plan.
/// a new query plan, i.e. an iterator indicating which host to try
/// first for querying, which one to use as failover, etc...
- public IEnumerable NewQueryPlan(Metadata metadata, string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
//shallow copy the all hosts
- var hosts = (from h in metadata.AllHosts() select h).ToArray();
+ var hosts = (from h in metadata.AllHostsSnapshot() select h).ToArray();
var startIndex = Interlocked.Increment(ref _index);
//Simplified overflow protection
diff --git a/src/Cassandra/Policies/TokenAwarePolicy.cs b/src/Cassandra/Policies/TokenAwarePolicy.cs
index b3bc0bb51..a97bb8de3 100644
--- a/src/Cassandra/Policies/TokenAwarePolicy.cs
+++ b/src/Cassandra/Policies/TokenAwarePolicy.cs
@@ -28,7 +28,7 @@ namespace Cassandra
///
///
/// The method is inherited from the child policy.
- /// The host yielded by the method will first return the
+ /// The host yielded by the method will first return the
/// replicas for the statement, based on the .
///
///
@@ -52,7 +52,7 @@ public TokenAwarePolicy(ILoadBalancingPolicy childPolicy)
public ILoadBalancingPolicy ChildPolicy { get; }
- public Task InitializeAsync(Metadata metadata)
+ public Task InitializeAsync(IMetadata metadata)
{
return ChildPolicy.InitializeAsync(metadata);
}
@@ -80,7 +80,7 @@ public HostDistance Distance(Host host)
/// Keyspace on which the query is going to be executed
/// the query for which to build the plan.
/// the new query plan.
- public IEnumerable NewQueryPlan(Metadata metadata, string loggedKeyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string loggedKeyspace, IStatement query)
{
var routingKey = query?.RoutingKey;
IEnumerable childIterator;
@@ -95,7 +95,7 @@ public IEnumerable NewQueryPlan(Metadata metadata, string loggedKeyspace,
}
var keyspace = query.Keyspace ?? loggedKeyspace;
- var replicas = metadata.GetReplicas(keyspace, routingKey.RawRoutingKey);
+ var replicas = metadata.GetReplicasSnapshot(keyspace, routingKey.RawRoutingKey);
var localReplicaSet = new HashSet();
var localReplicaList = new List(replicas.Count);
diff --git a/src/Cassandra/ProtocolVersion.cs b/src/Cassandra/ProtocolVersion.cs
index 4b2e18e29..560230d09 100644
--- a/src/Cassandra/ProtocolVersion.cs
+++ b/src/Cassandra/ProtocolVersion.cs
@@ -79,11 +79,11 @@ internal static class ProtocolVersionExtensions
///
/// Determines if the protocol version is supported by this driver.
///
- public static bool IsSupported(this ProtocolVersion version, Configuration config)
+ public static bool IsSupported(this ProtocolVersion version, bool allowBetaProtocolVersions)
{
if (version.IsBeta())
{
- return config.AllowBetaProtocolVersions;
+ return allowBetaProtocolVersions;
}
return version >= ProtocolVersion.MinSupported && version <= ProtocolVersion.MaxSupported;
@@ -94,7 +94,7 @@ public static bool IsSupported(this ProtocolVersion version, Configuration confi
/// Returns zero when there isn't a lower supported version.
///
///
- public static ProtocolVersion GetLowerSupported(this ProtocolVersion version, Configuration config)
+ public static ProtocolVersion GetLowerSupported(this ProtocolVersion version, bool allowBetaProtocolVersions)
{
var lowerVersion = version;
do
@@ -111,7 +111,7 @@ public static ProtocolVersion GetLowerSupported(this ProtocolVersion version, Co
{
lowerVersion = lowerVersion - 1;
}
- } while (lowerVersion > 0 && !lowerVersion.IsSupported(config));
+ } while (lowerVersion > 0 && !lowerVersion.IsSupported(allowBetaProtocolVersions));
return lowerVersion;
}
@@ -119,7 +119,8 @@ public static ProtocolVersion GetLowerSupported(this ProtocolVersion version, Co
///
/// Gets the highest supported protocol version collectively by the given hosts.
///
- public static ProtocolVersion GetHighestCommon(this ProtocolVersion version, Configuration config, IEnumerable hosts)
+ public static ProtocolVersion GetHighestCommon(
+ this ProtocolVersion version, bool allowBetaProtocolVersions, IEnumerable hosts)
{
var maxVersion = (byte)version;
var v3Requirement = false;
@@ -141,7 +142,7 @@ public static ProtocolVersion GetHighestCommon(this ProtocolVersion version, Con
{
// Anything 4.0.0+ has a max protocol version of V5 and requires at least V3.
v3Requirement = true;
- maxVersion = config.AllowBetaProtocolVersions
+ maxVersion = allowBetaProtocolVersions
? Math.Min((byte)ProtocolVersion.V5, maxVersion)
: Math.Min((byte)ProtocolVersion.V4, maxVersion);
maxVersionWith3OrMore = maxVersion;
diff --git a/src/Cassandra/QueryTrace.cs b/src/Cassandra/QueryTrace.cs
index c3df980f0..cd9ac9ef4 100644
--- a/src/Cassandra/QueryTrace.cs
+++ b/src/Cassandra/QueryTrace.cs
@@ -18,6 +18,7 @@
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
+using Cassandra.SessionManagement;
using Cassandra.Tasks;
namespace Cassandra
@@ -34,7 +35,7 @@ public class QueryTrace
{
private readonly object _fetchLock = new object();
private readonly Guid _traceId;
- private readonly ISession _session;
+ private readonly IInternalSession _session;
private IPAddress _coordinator;
private int _duration = int.MinValue;
private List _events;
@@ -164,7 +165,7 @@ public IPAddress ClientAddress
internal set { _clientAddress = value; }
}
- public QueryTrace(Guid traceId, ISession session)
+ internal QueryTrace(Guid traceId, IInternalSession session)
{
if (session == null)
{
@@ -232,7 +233,7 @@ internal async Task LoadAsync()
// mark as disconnected, guaranteeing that it wont make metadata fetches triggered by a property get
// ReSharper disable once InconsistentlySynchronizedField : Can be both async and sync, don't mind
_isDisconnected = false;
- var metadata = await _session.Cluster.GetMetadataAsync().ConfigureAwait(false);
+ var metadata = await _session.TryInitAndGetMetadataAsync().ConfigureAwait(false);
return await metadata.GetQueryTraceAsync(this).ConfigureAwait(false);
}
diff --git a/src/Cassandra/Requests/IRequestHandler.cs b/src/Cassandra/Requests/IRequestHandler.cs
index 3ad0f0d92..eb4c8c524 100644
--- a/src/Cassandra/Requests/IRequestHandler.cs
+++ b/src/Cassandra/Requests/IRequestHandler.cs
@@ -19,6 +19,7 @@
using System.Net;
using System.Threading.Tasks;
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Serialization;
@@ -29,7 +30,7 @@ namespace Cassandra.Requests
///
internal interface IRequestHandler
{
- Metadata Metadata { get; }
+ IInternalMetadata InternalMetadata { get; }
IRequestOptions RequestOptions { get; }
diff --git a/src/Cassandra/Requests/IRequestHandlerFactory.cs b/src/Cassandra/Requests/IRequestHandlerFactory.cs
index 2a48e85ef..b7d08f608 100644
--- a/src/Cassandra/Requests/IRequestHandlerFactory.cs
+++ b/src/Cassandra/Requests/IRequestHandlerFactory.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
@@ -24,15 +25,15 @@ internal interface IRequestHandlerFactory
{
IRequestHandler Create(
IInternalSession session,
- Metadata metadata,
+ IInternalMetadata internalMetadata,
ISerializer serializer,
IRequest request,
IStatement statement,
IRequestOptions options);
IRequestHandler Create(
- IInternalSession session, Metadata metadata, ISerializer serializer, IStatement statement, IRequestOptions options);
+ IInternalSession session, IInternalMetadata internalMetadata, ISerializer serializer, IStatement statement, IRequestOptions options);
- IRequestHandler Create(IInternalSession session, Metadata metadata, ISerializer serializer);
+ IRequestHandler Create(IInternalSession session, IInternalMetadata internalMetadata, ISerializer serializer);
}
}
\ No newline at end of file
diff --git a/src/Cassandra/Requests/PrepareHandler.cs b/src/Cassandra/Requests/PrepareHandler.cs
index 5c355f458..52794483a 100644
--- a/src/Cassandra/Requests/PrepareHandler.cs
+++ b/src/Cassandra/Requests/PrepareHandler.cs
@@ -163,7 +163,7 @@ private async Task GetPreparedStatement(
return ps;
}
- private static async Task FillRoutingInfo(PreparedStatement ps, InternalMetadata internalMetadata)
+ private static async Task FillRoutingInfo(PreparedStatement ps, IInternalMetadata internalMetadata)
{
var column = ps.Variables.Columns.FirstOrDefault();
if (column?.Keyspace == null)
diff --git a/src/Cassandra/Requests/ReprepareHandler.cs b/src/Cassandra/Requests/ReprepareHandler.cs
index b7d0618b1..3eb7357ab 100644
--- a/src/Cassandra/Requests/ReprepareHandler.cs
+++ b/src/Cassandra/Requests/ReprepareHandler.cs
@@ -31,7 +31,7 @@ public async Task ReprepareOnAllNodesWithExistingConnections(
IInternalSession session, PrepareRequest request, PrepareResult prepareResult)
{
var pools = session.GetPools();
- var metadata = await session.Cluster.GetMetadataAsync().ConfigureAwait(false);
+ var metadata = await session.TryInitAndGetMetadataAsync().ConfigureAwait(false);
var hosts = metadata.AllHosts();
var poolsByHosts = pools.Join(
hosts, po => po.Key,
diff --git a/src/Cassandra/Requests/RequestExecution.cs b/src/Cassandra/Requests/RequestExecution.cs
index 6f2dbce74..0476a9617 100644
--- a/src/Cassandra/Requests/RequestExecution.cs
+++ b/src/Cassandra/Requests/RequestExecution.cs
@@ -245,11 +245,11 @@ private void HandleSchemaChangeAsync(ResultResponse response, OutputSchemaChange
result,
async () =>
{
- var schemaAgreed = await _parent.Metadata.WaitForSchemaAgreementAsync(_connection).ConfigureAwait(false);
+ var schemaAgreed = await _parent.InternalMetadata.WaitForSchemaAgreementAsync(_connection).ConfigureAwait(false);
result.Info.SetSchemaInAgreement(schemaAgreed);
try
{
- await _parent.Metadata.ControlConnection
+ await _parent.InternalMetadata.ControlConnection
.HandleSchemaChangeEvent(schemaChange.SchemaChangeEventArgs, true)
.WaitToCompleteAsync(
_session.Cluster.Configuration.ProtocolOptions.MaxSchemaAgreementWaitSeconds * 1000)
@@ -329,7 +329,7 @@ private void SetAutoPage(RowSet rs, IInternalSession session)
var request = (IQueryRequest)_parent.BuildRequest();
request.PagingState = pagingState;
return _session.Cluster.Configuration.RequestHandlerFactory.Create(
- session, _parent.Metadata, _parent.Serializer, request, statement, _parent.RequestOptions).SendAsync();
+ session, _parent.InternalMetadata, _parent.Serializer, request, statement, _parent.RequestOptions).SendAsync();
}, _parent.RequestOptions.QueryAbortTimeout, _session.MetricsManager);
}
}
diff --git a/src/Cassandra/Requests/RequestHandler.cs b/src/Cassandra/Requests/RequestHandler.cs
index 36b469a9b..0ed9731b0 100644
--- a/src/Cassandra/Requests/RequestHandler.cs
+++ b/src/Cassandra/Requests/RequestHandler.cs
@@ -24,6 +24,7 @@
using Cassandra.Collections;
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Observers.Abstractions;
using Cassandra.Serialization;
@@ -58,21 +59,21 @@ internal class RequestHandler : IRequestHandler
public IRequestOptions RequestOptions { get; }
- public Metadata Metadata { get; }
+ public IInternalMetadata InternalMetadata { get; }
///
/// Creates a new instance using a request, the statement and the execution profile.
///
public RequestHandler(
IInternalSession session,
- Metadata metadata,
+ IInternalMetadata internalMetadata,
ISerializer serializer,
IRequest request,
IStatement statement,
IRequestOptions requestOptions)
{
_session = session ?? throw new ArgumentNullException(nameof(session));
- Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
+ InternalMetadata = internalMetadata ?? throw new ArgumentNullException(nameof(internalMetadata));
_requestObserver = session.ObserverFactory.CreateRequestObserver();
_requestResultHandler = new TcsMetricsRequestResultHandler(_requestObserver);
_request = request;
@@ -87,7 +88,8 @@ public RequestHandler(
RetryPolicy = statement.RetryPolicy.Wrap(RetryPolicy);
}
- _queryPlan = RequestHandler.GetQueryPlan(session, metadata, statement, RequestOptions.LoadBalancingPolicy).GetEnumerator();
+ _queryPlan = RequestHandler.GetQueryPlan(
+ session, _session.Cluster.Metadata, statement, RequestOptions.LoadBalancingPolicy).GetEnumerator();
}
///
@@ -96,13 +98,13 @@ public RequestHandler(
///
public RequestHandler(
IInternalSession session,
- Metadata metadata,
+ IInternalMetadata internalMetadata,
ISerializer serializer,
IStatement statement,
IRequestOptions requestOptions)
: this(
session,
- metadata,
+ internalMetadata,
serializer,
RequestHandler.GetRequest(statement, serializer, requestOptions),
statement,
@@ -113,8 +115,8 @@ public RequestHandler(
///
/// Creates a new instance with no request, suitable for getting a connection.
///
- public RequestHandler(IInternalSession session, Metadata metadata, ISerializer serializer)
- : this(session, metadata, serializer, null, null, session.Cluster.Configuration.DefaultRequestOptions)
+ public RequestHandler(IInternalSession session, IInternalMetadata internalMetadata, ISerializer serializer)
+ : this(session, internalMetadata, serializer, null, null, session.Cluster.Configuration.DefaultRequestOptions)
{
}
@@ -123,7 +125,7 @@ public RequestHandler(IInternalSession session, Metadata metadata, ISerializer s
/// In the special case when a Host is provided at Statement level, it will return a query plan with a single
/// host.
///
- private static IEnumerable GetQueryPlan(ISession session, Metadata metadata, IStatement statement, ILoadBalancingPolicy lbp)
+ private static IEnumerable GetQueryPlan(ISession session, IMetadata metadata, IStatement statement, ILoadBalancingPolicy lbp)
{
// Single host iteration
var host = (statement as Statement)?.Host;
@@ -523,7 +525,8 @@ private void ScheduleNext(Host currentHost)
}
if (_executionPlan == null)
{
- _executionPlan = RequestOptions.SpeculativeExecutionPolicy.NewPlan(_session.Keyspace, Statement);
+ _executionPlan = RequestOptions.SpeculativeExecutionPolicy.NewPlan(
+ _session.Cluster.Metadata, _session.Keyspace, Statement);
}
var delay = _executionPlan.NextExecution(currentHost);
if (delay <= 0)
diff --git a/src/Cassandra/Requests/RequestHandlerFactory.cs b/src/Cassandra/Requests/RequestHandlerFactory.cs
index 5a85d52a0..c6473aecb 100644
--- a/src/Cassandra/Requests/RequestHandlerFactory.cs
+++ b/src/Cassandra/Requests/RequestHandlerFactory.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
@@ -24,7 +25,7 @@ internal class RequestHandlerFactory : IRequestHandlerFactory
{
public IRequestHandler Create(
IInternalSession session,
- Metadata metadata,
+ IInternalMetadata metadata,
ISerializer serializer,
IRequest request,
IStatement statement,
@@ -34,12 +35,12 @@ public IRequestHandler Create(
}
public IRequestHandler Create(
- IInternalSession session, Metadata metadata, ISerializer serializer, IStatement statement, IRequestOptions options)
+ IInternalSession session, IInternalMetadata metadata, ISerializer serializer, IStatement statement, IRequestOptions options)
{
return new RequestHandler(session, metadata, serializer, statement, options);
}
- public IRequestHandler Create(IInternalSession session, Metadata metadata, ISerializer serializer)
+ public IRequestHandler Create(IInternalSession session, IInternalMetadata metadata, ISerializer serializer)
{
return new RequestHandler(session, metadata, serializer);
}
diff --git a/src/Cassandra/SchemaParser.cs b/src/Cassandra/SchemaParser.cs
index d8d59fa4c..d4c7aa347 100644
--- a/src/Cassandra/SchemaParser.cs
+++ b/src/Cassandra/SchemaParser.cs
@@ -19,6 +19,7 @@
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
using Cassandra.Serialization;
using Cassandra.Tasks;
using SortOrder = Cassandra.DataCollectionMetadata.SortOrder;
@@ -34,13 +35,13 @@ internal abstract class SchemaParser : ISchemaParser
private const string SelectTraceEvents = "SELECT * FROM system_traces.events WHERE session_id = {0}";
protected readonly IMetadataQueryProvider Cc;
- protected readonly Metadata Parent;
+ protected readonly IInternalMetadata Parent;
protected abstract string SelectAggregates { get; }
protected abstract string SelectFunctions { get; }
protected abstract string SelectTables { get; }
protected abstract string SelectUdts { get; }
- protected SchemaParser(Metadata parent)
+ protected SchemaParser(IInternalMetadata parent)
{
Cc = parent.ControlConnection;
Parent = parent;
@@ -165,7 +166,7 @@ internal class SchemaParserV1 : SchemaParser
protected override string SelectUdts => "SELECT * FROM system.schema_usertypes WHERE keyspace_name='{0}' AND type_name = '{1}'";
- internal SchemaParserV1(Metadata parent) : base(parent)
+ internal SchemaParserV1(IInternalMetadata parent) : base(parent)
{
}
@@ -536,7 +537,7 @@ internal class SchemaParserV2 : SchemaParser
protected override string SelectUdts => "SELECT * FROM system_schema.types WHERE keyspace_name='{0}' AND type_name = '{1}'";
- internal SchemaParserV2(Metadata parent, Func> udtResolver)
+ internal SchemaParserV2(IInternalMetadata parent, Func> udtResolver)
: base(parent)
{
_udtResolver = udtResolver;
@@ -914,7 +915,7 @@ internal class SchemaParserV3 : SchemaParserV2
"SELECT * FROM system_virtual_schema.columns WHERE keyspace_name = '{0}' AND table_name='{1}'";
private const string SelectVirtualKeyspaceNames = "SELECT keyspace_name FROM system_virtual_schema.keyspaces";
- internal SchemaParserV3(Metadata parent, Func> udtResolver)
+ internal SchemaParserV3(IInternalMetadata parent, Func> udtResolver)
: base(parent, udtResolver)
{
diff --git a/src/Cassandra/Session.cs b/src/Cassandra/Session.cs
index 815b397dd..3dbfef0d3 100644
--- a/src/Cassandra/Session.cs
+++ b/src/Cassandra/Session.cs
@@ -23,6 +23,7 @@
using Cassandra.Collections;
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Graph;
using Cassandra.DataStax.Insights;
using Cassandra.ExecutionProfiles;
@@ -48,10 +49,9 @@ public class Session : IInternalSession
private readonly IObserverFactory _observerFactory;
private readonly IInsightsClient _insightsClient;
- private readonly Task _initTask;
+ private readonly Task _initTask;
private long _state = Session.Initializing;
- private volatile Metadata _initializedMetadata;
private volatile string _keyspace;
internal IInternalSession InternalRef => this;
@@ -101,7 +101,7 @@ string IInternalSession.Keyspace
///
public void Connect()
{
- TaskHelper.WaitToComplete(ConnectAsync());
+ InternalRef.TryInit();
}
///
@@ -228,8 +228,7 @@ private async Task ShutdownInternalAsync()
_metricsManager?.Dispose();
- var metadata = _initializedMetadata;
- metadata.HostRemoved -= OnHostRemoved;
+ _cluster.Metadata.HostRemoved -= OnHostRemoved;
var pools = _connectionPool.ToArray();
foreach (var pool in pools)
@@ -238,14 +237,35 @@ private async Task ShutdownInternalAsync()
}
}
- Task IInternalSession.TryInitAndGetMetadataAsync()
+ Task IInternalSession.TryInitAndGetMetadataAsync()
+ {
+ ValidateState();
+ return _initTask;
+ }
+
+ void IInternalSession.TryInit()
+ {
+ if (ValidateState())
+ {
+ return;
+ }
+
+ TaskHelper.WaitToComplete(_initTask);
+ }
+
+ ///
+ /// Validates if the session is not disposed and checks whether it is already initialized.
+ /// Throws the initialization exception if the initialization failed.
+ ///
+ /// true if session is initialized, false otherwise
+ private bool ValidateState()
{
var currentState = Interlocked.Read(ref _state);
if (currentState == Session.Initialized)
{
//It was already initialized
- return _initTask;
+ return true;
}
if (currentState == Session.Disposed)
@@ -259,32 +279,32 @@ Task IInternalSession.TryInitAndGetMetadataAsync()
throw _cluster.InitException;
}
- return _initTask;
+ return false;
}
-
- private async Task InitInternalAsync()
+
+ private async Task InitInternalAsync()
{
- var metadata = await InternalRef.InternalCluster.TryInitAndGetMetadataAsync().ConfigureAwait(false);
- metadata.HostRemoved += OnHostRemoved;
+ var internalMetadata = await InternalRef.InternalCluster.TryInitAndGetMetadataAsync().ConfigureAwait(false);
+
+ InternalRef.InternalCluster.Metadata.HostRemoved += OnHostRemoved;
_metricsManager.InitializeMetrics(this);
- var serializerManager = _cluster.SerializerManager;
+ var serializerManager = _cluster.Configuration.SerializerManager;
if (Configuration.GetOrCreatePoolingOptions(serializerManager.CurrentProtocolVersion).GetWarmup())
{
- await WarmupAsync().ConfigureAwait(false);
+ await WarmupAsync(internalMetadata).ConfigureAwait(false);
}
if (Keyspace != null)
{
// Borrow a connection, trying to fail fast
- var handler = Configuration.RequestHandlerFactory.Create(this, metadata, serializerManager.GetCurrentSerializer());
+ var handler = Configuration.RequestHandlerFactory.Create(this, internalMetadata, serializerManager.GetCurrentSerializer());
await handler.GetNextConnectionAsync(new Dictionary()).ConfigureAwait(false);
}
- await _insightsClient.InitializeAsync().ConfigureAwait(false);
+ _insightsClient.Initialize(internalMetadata);
- _initializedMetadata = metadata;
var previousState = Interlocked.CompareExchange(ref _state, Session.Initialized, Session.Initializing);
if (previousState == Session.Disposed)
{
@@ -292,7 +312,7 @@ private async Task InitInternalAsync()
throw new ObjectDisposedException("Session instance was disposed before initialization finished.");
}
- return metadata;
+ return internalMetadata;
}
///
@@ -300,9 +320,8 @@ private async Task InitInternalAsync()
/// Returns a Task that is marked as completed after all pools were warmed up.
/// In case, all the host pool warmup fail, it logs an error.
///
- private async Task WarmupAsync()
+ private async Task WarmupAsync(IInternalMetadata metadata)
{
- var metadata = await _cluster.GetMetadataAsync().ConfigureAwait(false);
var hosts = metadata.AllHosts().Where(h => _cluster.RetrieveAndSetDistance(h) == HostDistance.Local).ToArray();
var tasks = new Task[hosts.Length];
for (var i = 0; i < hosts.Length; i++)
@@ -401,7 +420,7 @@ private async Task ExecuteAsync(IStatement statement, IRequestOptions re
return await ExecuteAsync(metadata, statement, requestOptions).ConfigureAwait(false);
}
- private Task ExecuteAsync(Metadata metadata, IStatement statement, IRequestOptions requestOptions)
+ private Task ExecuteAsync(IInternalMetadata metadata, IStatement statement, IRequestOptions requestOptions)
{
return Configuration.RequestHandlerFactory
.Create(this, metadata, metadata.SerializerManager.GetCurrentSerializer(), statement, requestOptions)
@@ -414,7 +433,7 @@ IHostConnectionPool IInternalSession.GetOrCreateConnectionPool(Host host, HostDi
var hostPool = _connectionPool.GetOrAdd(host.Address, address =>
{
var newPool = Configuration.HostConnectionPoolFactory.Create(
- host, Configuration, _cluster.SerializerManager, _observerFactory);
+ host, Configuration, _cluster.Configuration.SerializerManager, _observerFactory);
newPool.AllConnectionClosed += InternalRef.OnAllConnectionClosed;
newPool.SetDistance(distance);
_metricsManager.GetOrCreateNodeMetrics(host).InitializePoolGauges(newPool);
@@ -582,7 +601,7 @@ public async Task ExecuteGraphAsync(IGraphStatement graphStateme
}
private async Task GetAnalyticsPrimary(
- Metadata metadata, IStatement statement, IGraphStatement graphStatement, IRequestOptions requestOptions)
+ IInternalMetadata internalMetadata, IStatement statement, IGraphStatement graphStatement, IRequestOptions requestOptions)
{
if (!(statement is TargettedSimpleStatement) || !requestOptions.GraphOptions.IsAnalyticsQuery(graphStatement))
{
@@ -603,10 +622,10 @@ private async Task GetAnalyticsPrimary(
return statement;
}
- return AdaptRpcPrimaryResult(metadata, rs, targetedSimpleStatement);
+ return AdaptRpcPrimaryResult(internalMetadata, rs, targetedSimpleStatement);
}
- private IStatement AdaptRpcPrimaryResult(Metadata metadata, RowSet rowSet, TargettedSimpleStatement statement)
+ private IStatement AdaptRpcPrimaryResult(IInternalMetadata internalMetadata, RowSet rowSet, TargettedSimpleStatement statement)
{
var row = rowSet.FirstOrDefault();
if (row == null)
@@ -624,7 +643,7 @@ private IStatement AdaptRpcPrimaryResult(Metadata metadata, RowSet rowSet, Targe
var hostName = location.Substring(0, location.LastIndexOf(':'));
var address = Configuration.AddressTranslator.Translate(
new IPEndPoint(IPAddress.Parse(hostName), Configuration.ProtocolOptions.Port));
- var host = metadata.GetHost(address);
+ var host = internalMetadata.GetHost(address);
statement.PreferredHost = host;
return statement;
}
diff --git a/src/Cassandra/SessionManagement/IInternalCluster.cs b/src/Cassandra/SessionManagement/IInternalCluster.cs
index ba53b7e82..b92c4e120 100644
--- a/src/Cassandra/SessionManagement/IInternalCluster.cs
+++ b/src/Cassandra/SessionManagement/IInternalCluster.cs
@@ -18,22 +18,19 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
+
using Cassandra.Connections;
using Cassandra.Connections.Control;
-using Cassandra.Requests;
-using Cassandra.Serialization;
namespace Cassandra.SessionManagement
{
///
internal interface IInternalCluster : ICluster
{
- ISerializerManager SerializerManager { get; }
-
IInternalMetadata InternalMetadata { get; }
bool AnyOpenConnections(Host host);
-
+
///
/// If contact points are not provided in the builder, the driver will use localhost
/// as an implicit contact point.
@@ -54,17 +51,17 @@ internal interface IInternalCluster : ICluster
/// In case the statement was already in the prepared statements cache, logs an warning but prepares it anyway.
///
Task PrepareAsync(IInternalSession session, string cqlQuery, string keyspace, IDictionary customPayload);
-
+
IReadOnlyDictionary> GetResolvedEndpoints();
///
/// Helper method to retrieve the aggregate distance from all configured LoadBalancingPolicies and set it at Host level.
///
HostDistance RetrieveAndSetDistance(Host host);
-
+
///
- /// Initializes once (Thread-safe) the control connection
+ /// Initializes once (Thread-safe) the control connection and retrieve the internal metadata object.
///
- Task TryInitializeAsync();
+ Task TryInitAndGetMetadataAsync();
}
}
\ No newline at end of file
diff --git a/src/Cassandra/SessionManagement/IInternalSession.cs b/src/Cassandra/SessionManagement/IInternalSession.cs
index 5629a7770..96b143dd8 100644
--- a/src/Cassandra/SessionManagement/IInternalSession.cs
+++ b/src/Cassandra/SessionManagement/IInternalSession.cs
@@ -20,6 +20,7 @@
using System.Threading.Tasks;
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Metrics.Internal;
using Cassandra.Observers.Abstractions;
@@ -35,8 +36,10 @@ internal interface IInternalSession : ISession
/// Unique Session ID generated by the driver.
///
Guid InternalSessionId { get; }
+
+ void TryInit();
- Task TryInitAndGetMetadataAsync();
+ Task TryInitAndGetMetadataAsync();
///
/// Gets or creates the connection pool for a given host
diff --git a/src/Cassandra/SessionState.cs b/src/Cassandra/SessionState.cs
index 858261623..6da030447 100644
--- a/src/Cassandra/SessionState.cs
+++ b/src/Cassandra/SessionState.cs
@@ -18,6 +18,7 @@
using System.Text;
using Cassandra.Collections;
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
namespace Cassandra
@@ -62,13 +63,13 @@ public override string ToString()
return builder.ToString();
}
- internal static SessionState From(IInternalSession session, Metadata metadata)
+ internal static SessionState From(IInternalSession session, IInternalMetadata internalMetadata)
{
var pools = session.GetPools();
var result = new Dictionary();
foreach (var kv in pools)
{
- var host = metadata.GetHost(kv.Key);
+ var host = internalMetadata.GetHost(kv.Key);
if (host == null)
{
continue;
diff --git a/src/Cassandra/UdtMappingDefinitions.cs b/src/Cassandra/UdtMappingDefinitions.cs
index 2a1c25d6d..3f7ba38ac 100644
--- a/src/Cassandra/UdtMappingDefinitions.cs
+++ b/src/Cassandra/UdtMappingDefinitions.cs
@@ -18,7 +18,7 @@
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
-
+using Cassandra.Connections.Control;
using Cassandra.SessionManagement;
using Cassandra.Tasks;
@@ -59,7 +59,7 @@ public async Task DefineAsync(params UdtMap[] udtMaps)
{
throw new ArgumentNullException("udtMaps");
}
- var metadata = await _cluster.GetMetadataAsync().ConfigureAwait(false);
+ var metadata = await _session.TryInitAndGetMetadataAsync().ConfigureAwait(false);
var sessionKeyspace = _session.Keyspace;
if (string.IsNullOrEmpty(sessionKeyspace) && udtMaps.Any(map => map.Keyspace == null))
{
@@ -86,7 +86,7 @@ public async Task DefineAsync(params UdtMap[] udtMaps)
/// Gets the definition and validates the fields
///
///
- private async Task GetDefinitionAsync(string keyspace, UdtMap map, Metadata metadata)
+ private async Task GetDefinitionAsync(string keyspace, UdtMap map, IInternalMetadata metadata)
{
var caseSensitiveUdtName = map.UdtName;
if (map.IgnoreCase)
From 5af864c2edce849597a4a39e7e2c11b153386f55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Wed, 1 Jul 2020 19:32:02 +0100
Subject: [PATCH 10/31] add Metadata parameter to LBP.Distance
---
src/Cassandra/Cluster.cs | 4 ++--
src/Cassandra/ClusterDescription.cs | 10 ++++++----
src/Cassandra/Configuration.cs | 5 +++--
src/Cassandra/Connections/Control/IInternalMetadata.cs | 4 +++-
src/Cassandra/Connections/Control/InternalMetadata.cs | 8 +++++++-
src/Cassandra/Connections/Control/TopologyRefresher.cs | 2 +-
.../StartupMessage/DataCentersInfoProvider.cs | 5 +++--
src/Cassandra/Metadata.cs | 5 ++---
src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs | 3 ++-
src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs | 5 +++--
src/Cassandra/Policies/ILoadBalancingPolicy.cs | 7 ++++---
src/Cassandra/Policies/RetryLoadBalancingPolicy.cs | 4 ++--
src/Cassandra/Policies/RoundRobinPolicy.cs | 3 ++-
src/Cassandra/Policies/TokenAwarePolicy.cs | 9 +++++----
14 files changed, 45 insertions(+), 29 deletions(-)
diff --git a/src/Cassandra/Cluster.cs b/src/Cassandra/Cluster.cs
index 1d36ea1ea..bb783c145 100644
--- a/src/Cassandra/Cluster.cs
+++ b/src/Cassandra/Cluster.cs
@@ -475,12 +475,12 @@ private async Task ShutdownInternalAsync()
///
HostDistance IInternalCluster.RetrieveAndSetDistance(Host host)
{
- var distance = _loadBalancingPolicies[0].Distance(host);
+ var distance = _loadBalancingPolicies[0].Distance(Metadata, host);
for (var i = 1; i < _loadBalancingPolicies.Count; i++)
{
var lbp = _loadBalancingPolicies[i];
- var lbpDistance = lbp.Distance(host);
+ var lbpDistance = lbp.Distance(Metadata, host);
if (lbpDistance < distance)
{
distance = lbpDistance;
diff --git a/src/Cassandra/ClusterDescription.cs b/src/Cassandra/ClusterDescription.cs
index 44de74e8f..d7e161c3c 100644
--- a/src/Cassandra/ClusterDescription.cs
+++ b/src/Cassandra/ClusterDescription.cs
@@ -14,6 +14,8 @@
// limitations under the License.
//
+using Cassandra.Connections.Control;
+
namespace Cassandra
{
///
@@ -22,11 +24,11 @@ namespace Cassandra
public class ClusterDescription
{
internal ClusterDescription(
- string clusterName, bool isDbaas, ProtocolVersion protocolVersion)
+ IInternalMetadata internalMetadata)
{
- ClusterName = clusterName;
- IsDbaas = isDbaas;
- ProtocolVersion = protocolVersion;
+ ClusterName = internalMetadata.ClusterName;
+ IsDbaas = internalMetadata.IsDbaas;
+ ProtocolVersion = internalMetadata.ProtocolVersion;
}
///
diff --git a/src/Cassandra/Configuration.cs b/src/Cassandra/Configuration.cs
index 516cb689f..db5bc479f 100644
--- a/src/Cassandra/Configuration.cs
+++ b/src/Cassandra/Configuration.cs
@@ -366,7 +366,8 @@ internal Configuration(Policies policies,
ISupportedOptionsInitializerFactory supportedOptionsInitializerFactory = null,
IProtocolVersionNegotiator protocolVersionNegotiator = null,
IServerEventsSubscriber serverEventsSubscriber = null,
- ILocalDatacenterProvider localDatacenterProvider = null)
+ ILocalDatacenterProvider localDatacenterProvider = null,
+ ISerializerManager serializerManager = null)
{
AddressTranslator = addressTranslator ?? throw new ArgumentNullException(nameof(addressTranslator));
QueryOptions = queryOptions ?? throw new ArgumentNullException(nameof(queryOptions));
@@ -438,7 +439,7 @@ internal Configuration(Policies policies,
protocolVersion = ProtocolOptions.MaxProtocolVersionValue.Value;
}
- SerializerManager = new SerializerManager(protocolVersion, TypeSerializers);
+ SerializerManager = serializerManager ?? new SerializerManager(protocolVersion, TypeSerializers);
}
///
diff --git a/src/Cassandra/Connections/Control/IInternalMetadata.cs b/src/Cassandra/Connections/Control/IInternalMetadata.cs
index b9ec24d5d..8b6cbae88 100644
--- a/src/Cassandra/Connections/Control/IInternalMetadata.cs
+++ b/src/Cassandra/Connections/Control/IInternalMetadata.cs
@@ -38,7 +38,7 @@ internal interface IInternalMetadata
ISchemaParser SchemaParser { get; }
- string Partitioner { get; set; }
+ string Partitioner { get; }
Hosts Hosts { get; }
@@ -222,6 +222,8 @@ void SetResolvedContactPoints(
void SetClusterName(string clusterName);
+ void SetPartitioner(string partitioner);
+
IEnumerable UpdateResolvedContactPoint(
IContactPoint contactPoint, IEnumerable endpoints);
}
diff --git a/src/Cassandra/Connections/Control/InternalMetadata.cs b/src/Cassandra/Connections/Control/InternalMetadata.cs
index 985e68e7d..0a0d4fa8a 100644
--- a/src/Cassandra/Connections/Control/InternalMetadata.cs
+++ b/src/Cassandra/Connections/Control/InternalMetadata.cs
@@ -47,6 +47,7 @@ internal class InternalMetadata : IInternalMetadata
new CopyOnWriteDictionary>();
private volatile bool _isDbaas = false;
private volatile string _clusterName;
+ private volatile string _partitioner;
///
public Configuration Configuration { get; }
@@ -58,7 +59,7 @@ internal class InternalMetadata : IInternalMetadata
public ISchemaParser SchemaParser => _schemaParser;
- public string Partitioner { get; set; }
+ public string Partitioner => _partitioner;
public Hosts Hosts { get; }
@@ -677,6 +678,11 @@ public void SetClusterName(string clusterName)
_clusterName = clusterName;
}
+ public void SetPartitioner(string partitioner)
+ {
+ _partitioner = partitioner;
+ }
+
public IEnumerable UpdateResolvedContactPoint(IContactPoint contactPoint, IEnumerable endpoints)
{
return _resolvedContactPoints.AddOrUpdate(contactPoint, _ => endpoints, (_, __) => endpoints);
diff --git a/src/Cassandra/Connections/Control/TopologyRefresher.cs b/src/Cassandra/Connections/Control/TopologyRefresher.cs
index f151a28b5..2252f4731 100644
--- a/src/Cassandra/Connections/Control/TopologyRefresher.cs
+++ b/src/Cassandra/Connections/Control/TopologyRefresher.cs
@@ -73,7 +73,7 @@ public async Task RefreshNodeListAsync(
throw new DriverInternalError("Local host metadata could not be retrieved");
}
- _internalMetadata.Partitioner = localRow.GetValue("partitioner");
+ _internalMetadata.SetPartitioner(localRow.GetValue("partitioner"));
var host = GetAndUpdateLocalHost(currentEndPoint, localRow);
UpdatePeersInfo(localIsPeersV2, rsPeers, host);
ControlConnection.Logger.Info("Node list retrieved successfully");
diff --git a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs
index c612e94a4..78ce92b7c 100644
--- a/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs
+++ b/src/Cassandra/DataStax/Insights/InfoProviders/StartupMessage/DataCentersInfoProvider.cs
@@ -23,7 +23,8 @@ namespace Cassandra.DataStax.Insights.InfoProviders.StartupMessage
{
internal class DataCentersInfoProvider : IInsightsInfoProvider>
{
- public HashSet GetInformation(IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
+ public HashSet GetInformation(
+ IInternalCluster cluster, IInternalSession session, IInternalMetadata internalMetadata)
{
var dataCenters = new HashSet();
var remoteConnectionsLength =
@@ -39,7 +40,7 @@ public HashSet GetInformation(IInternalCluster cluster, IInternalSession
continue;
}
- var distance = cluster.Configuration.Policies.LoadBalancingPolicy.Distance(h);
+ var distance = cluster.Configuration.Policies.LoadBalancingPolicy.Distance(cluster.Metadata, h);
if (distance == HostDistance.Local || (distance == HostDistance.Remote && remoteConnectionsLength > 0))
{
dataCenters.Add(h.Datacenter);
diff --git a/src/Cassandra/Metadata.cs b/src/Cassandra/Metadata.cs
index 433b658ad..3e9b86b61 100644
--- a/src/Cassandra/Metadata.cs
+++ b/src/Cassandra/Metadata.cs
@@ -41,7 +41,7 @@ internal class Metadata : IMetadata
private long _state = Metadata.NotInitialized;
- internal InternalMetadata InternalMetadata { get; }
+ internal IInternalMetadata InternalMetadata { get; }
internal Metadata(
IInternalCluster cluster,
@@ -121,8 +121,7 @@ public ICollection GetReplicasSnapshot(byte[] partitionKey)
private ClusterDescription GetClusterDescriptionInternal()
{
- return new ClusterDescription(
- InternalMetadata.ClusterName, InternalMetadata.IsDbaas, InternalMetadata.ProtocolVersion);
+ return new ClusterDescription(InternalMetadata);
}
///
diff --git a/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs b/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
index 7ee2aed15..fdd171d72 100644
--- a/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/DCAwareRoundRobinPolicy.cs
@@ -97,9 +97,10 @@ public Task InitializeAsync(IMetadata metadata)
/// is Ignored.
To configure how many host in each remote
/// datacenter is considered Remote.
///
+ /// The information about the session instance for which the policy is created.
/// the host of which to return the distance of.
/// the HostDistance to host.
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
var dc = GetDatacenter(host);
return dc == LocalDc ? HostDistance.Local : HostDistance.Remote;
diff --git a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
index 7b4237197..8524b7e07 100644
--- a/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/DefaultLoadBalancingPolicy.cs
@@ -67,9 +67,10 @@ public DefaultLoadBalancingPolicy(string localDc)
/// in the local datacenter as Local and the rest
/// is Ignored.
///
+ /// The information about the session instance for which the policy is created.
/// the host of which to return the distance of.
/// the HostDistance to host.
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
var lastPreferredHost = _lastPreferredHost;
if (lastPreferredHost != null && host == lastPreferredHost)
@@ -79,7 +80,7 @@ public HostDistance Distance(Host host)
return HostDistance.Local;
}
- return ChildPolicy.Distance(host);
+ return ChildPolicy.Distance(metadata, host);
}
///
diff --git a/src/Cassandra/Policies/ILoadBalancingPolicy.cs b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
index dca459aba..c5d7185d9 100644
--- a/src/Cassandra/Policies/ILoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/ILoadBalancingPolicy.cs
@@ -38,7 +38,7 @@ public interface ILoadBalancingPolicy
/// before any call to another of the methods of the policy.
///
///
- /// The information about the session instance for which the policy is created.
+ /// Metadata instance of the session for which the policy is created.
Task InitializeAsync(IMetadata metadata);
///
@@ -52,10 +52,11 @@ public interface ILoadBalancingPolicy
/// host in remote datacenters when the policy itself always picks host in the
/// local datacenter first.
///
+ /// Metadata instance of the session for which the policy is created.
/// the host of which to return the distance of.
///
/// the HostDistance to host.
- HostDistance Distance(Host host);
+ HostDistance Distance(IMetadata metadata, Host host);
///
/// Returns the hosts to use for a new query.
Each new query will call this
@@ -64,7 +65,7 @@ public interface ILoadBalancingPolicy
/// be so), the next host will be used. If all hosts of the returned
/// Iterator are down, the query will fail.
///
- /// The information about the session instance for which the policy is created.
+ /// Metadata instance of the session for which the policy is created.
/// The query for which to build a plan, it can be null.
/// Keyspace on which the query is going to be executed, it can be null.
/// An iterator of Host. The query is tried against the hosts returned
diff --git a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
index 8bfc38c93..ff255336e 100644
--- a/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
+++ b/src/Cassandra/Policies/RetryLoadBalancingPolicy.cs
@@ -40,9 +40,9 @@ public Task InitializeAsync(IMetadata metadata)
return LoadBalancingPolicy.InitializeAsync(metadata);
}
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
- return LoadBalancingPolicy.Distance(host);
+ return LoadBalancingPolicy.Distance(metadata, host);
}
public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
diff --git a/src/Cassandra/Policies/RoundRobinPolicy.cs b/src/Cassandra/Policies/RoundRobinPolicy.cs
index 0663349dd..54b517533 100644
--- a/src/Cassandra/Policies/RoundRobinPolicy.cs
+++ b/src/Cassandra/Policies/RoundRobinPolicy.cs
@@ -49,9 +49,10 @@ public Task InitializeAsync(IMetadata metadata)
/// datacenter deployment. If you use multiple datacenter, see
/// DCAwareRoundRobinPolicy instead.
///
+ /// The information about the session instance for which the policy is created.
/// the host of which to return the distance of.
/// the HostDistance to host.
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
return HostDistance.Local;
}
diff --git a/src/Cassandra/Policies/TokenAwarePolicy.cs b/src/Cassandra/Policies/TokenAwarePolicy.cs
index a97bb8de3..8e40437cc 100644
--- a/src/Cassandra/Policies/TokenAwarePolicy.cs
+++ b/src/Cassandra/Policies/TokenAwarePolicy.cs
@@ -27,7 +27,7 @@ namespace Cassandra
/// This policy encapsulates another policy. The resulting policy works in the following way:
///
///
- /// The method is inherited from the child policy.
+ /// The method is inherited from the child policy.
/// The host yielded by the method will first return the
/// replicas for the statement, based on the .
///
@@ -60,13 +60,14 @@ public Task InitializeAsync(IMetadata metadata)
///
/// Return the HostDistance for the provided host.
///
+ /// The information about the session instance for which the policy is created.
/// the host of which to return the distance of.
///
/// the HostDistance to host as returned by the wrapped
/// policy.
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
- return ChildPolicy.Distance(host);
+ return ChildPolicy.Distance(metadata, host);
}
///
@@ -100,7 +101,7 @@ public IEnumerable NewQueryPlan(IMetadata metadata, string loggedKeyspace,
var localReplicaSet = new HashSet();
var localReplicaList = new List(replicas.Count);
// We can't do it lazily as we need to balance the load between local replicas
- foreach (var localReplica in replicas.Where(h => ChildPolicy.Distance(h) == HostDistance.Local))
+ foreach (var localReplica in replicas.Where(h => ChildPolicy.Distance(metadata, h) == HostDistance.Local))
{
localReplicaSet.Add(localReplica);
localReplicaList.Add(localReplica);
From 8e07076887f3014ad35170c24fd86ceb1517b327 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Reis?=
Date: Wed, 1 Jul 2020 19:34:09 +0100
Subject: [PATCH 11/31] wip on tests
---
.../Core/ClusterTests.cs | 32 +++----
.../Core/PoolShortTests.cs | 71 ++++++++-------
.../Linq/LinqTable/CreateTable.cs | 4 +-
src/Cassandra.Tests/BaseUnitTest.cs | 9 +-
src/Cassandra.Tests/BuilderTests.cs | 2 +-
src/Cassandra.Tests/ClusterTests.cs | 17 ++--
.../Control/ControlConnectionTests.cs | 67 ++++++--------
.../Control/TopologyRefresherTests.cs | 43 +++++----
.../Connections/HostConnectionPoolTests.cs | 2 +-
.../FakeControlConnectionFactory.cs | 8 +-
.../FakeProtocolVersionNegotiator.cs | 12 ++-
.../FakeSupportedOptionsInitializer.cs | 6 +-
.../FakeSupportedOptionsInitializerFactory.cs | 4 +-
.../TestHelpers/FakeTopologyRefresher.cs | 20 ++--
.../FakeTopologyRefresherFactory.cs | 4 +-
.../DataStax/Insights/InsightsClientTests.cs | 63 ++++++-------
.../Insights/InsightsSupportVerifierTests.cs | 91 ++++++++++---------
.../InsightsMessageFactoryTests.cs | 39 ++++----
.../ExecutionProfiles/ClusterTests.cs | 21 +++--
.../ExecutionProfiles/RequestHandlerTests.cs | 24 +++--
.../ExecutionProfiles/SessionTests.cs | 25 +++--
.../Mapping/Linq/LinqEntryPointsTests.cs | 21 ++---
.../Mapping/Linq/LinqMappingUnitTests.cs | 15 ++-
.../Mapping/Linq/LinqToCqlUnitTests.cs | 12 +--
.../TestHelpers/FakeSchemaParserFactory.cs | 12 ++-
src/Cassandra.Tests/ProtocolTests.cs | 19 ++--
src/Cassandra.Tests/RequestExecutionTests.cs | 5 +
src/Cassandra.Tests/RequestHandlerTests.cs | 12 +--
.../Requests/FakeRequestHandlerFactory.cs | 23 ++++-
.../Requests/PrepareHandlerTests.cs | 10 +-
.../TestConfigurationBuilder.cs | 5 +-
src/Cassandra.Tests/TestHelper.cs | 37 ++++----
32 files changed, 389 insertions(+), 346 deletions(-)
diff --git a/src/Cassandra.IntegrationTests/Core/ClusterTests.cs b/src/Cassandra.IntegrationTests/Core/ClusterTests.cs
index 5906e2408..a8e3db90b 100644
--- a/src/Cassandra.IntegrationTests/Core/ClusterTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/ClusterTests.cs
@@ -24,6 +24,7 @@
using Cassandra.SessionManagement;
using Cassandra.IntegrationTests.TestClusterManagement;
using Cassandra.IntegrationTests.TestClusterManagement.Simulacron;
+using Cassandra.Tasks;
using Cassandra.Tests;
using NUnit.Framework;
@@ -115,7 +116,7 @@ public async Task Cluster_Should_Honor_MaxProtocolVersion_Set(bool asyncConnect)
.AddContactPoint(_testCluster.InitialContactPoint)
.Build())
{
- Assert.AreEqual(Cluster.MaxProtocolVersion, clusterDefault.Configuration.ProtocolOptions.MaxProtocolVersion);
+ Assert.AreEqual(Configuration.MaxProtocolVersion, clusterDefault.Configuration.ProtocolOptions.MaxProtocolVersion);
// MaxProtocolVersion set
var clusterMax = ClusterBuilder()
@@ -126,9 +127,9 @@ public async Task Cluster_Should_Honor_MaxProtocolVersion_Set(bool asyncConnect)
await Connect(clusterMax, asyncConnect, session =>
{
if (TestClusterManager.CheckCassandraVersion(false, Version.Parse("2.1"), Comparison.LessThan))
- Assert.AreEqual(2, session.BinaryProtocolVersion);
+ Assert.AreEqual(2, session.Cluster.Metadata.GetClusterDescription().ProtocolVersion);
else
- Assert.AreEqual(3, session.BinaryProtocolVersion);
+ Assert.AreEqual(3, session.Cluster.Metadata.GetClusterDescription().ProtocolVersion);
}).ConfigureAwait(false);
// Arbitary MaxProtocolVersion set, will negotiate down upon connect
@@ -163,7 +164,7 @@ public async Task Should_Add_And_Query_Newly_Bootstrapped_Node()
{
await Connect(cluster, false, session =>
{
- Assert.AreEqual(1, cluster.AllHosts().Count);
+ Assert.AreEqual(1, cluster.Metadata.AllHosts().Count);
_realCluster.BootstrapNode(2);
Trace.TraceInformation("Node bootstrapped");
var newNodeAddress = _realCluster.ClusterIpPrefix + 2;
@@ -172,8 +173,8 @@ await Connect(cluster, false, session =>
{
Assert.True(TestUtils.IsNodeReachable(newNodeIpAddress));
//New node should be part of the metadata
- Assert.AreEqual(2, cluster.AllHosts().Count);
- var host = cluster.AllHosts().FirstOrDefault(h => h.Address.Address.Equals(newNodeIpAddress));
+ Assert.AreEqual(2, cluster.Metadata.AllHosts().Count);
+ var host = cluster.Metadata.AllHosts().FirstOrDefault(h => h.Address.Address.Equals(newNodeIpAddress));
Assert.IsNotNull(host);
},
2000,
@@ -200,7 +201,7 @@ public async Task Should_Remove_Decommissioned_Node()
{
await Connect(cluster, false, session =>
{
- Assert.AreEqual(numberOfNodes, cluster.AllHosts().Count);
+ Assert.AreEqual(numberOfNodes, cluster.Metadata.AllHosts().Count);
if (TestClusterManager.SupportsDecommissionForcefully())
{
_realCluster.DecommissionNodeForcefully(numberOfNodes);
@@ -217,7 +218,7 @@ await Connect(cluster, false, session =>
decommisionedNode = _realCluster.ClusterIpPrefix + 2;
Assert.False(TestUtils.IsNodeReachable(IPAddress.Parse(decommisionedNode)));
//New node should be part of the metadata
- Assert.AreEqual(1, cluster.AllHosts().Count);
+ Assert.AreEqual(1, cluster.Metadata.AllHosts().Count);
}, 100, 100);
var queried = false;
for (var i = 0; i < 10; i++)
@@ -234,25 +235,24 @@ await Connect(cluster, false, session =>
}
}
- private class TestLoadBalancingPolicy : ILoadBalancingPolicy
+ private class TestLoadBalancingPolicy : ILoadBalancingPolicy
{
- private ICluster _cluster;
public Host ControlConnectionHost { get; private set; }
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
- _cluster = cluster;
- ControlConnectionHost = ((IInternalCluster)cluster).GetControlConnection().Host;
+ ControlConnectionHost = ((Metadata)metadata).InternalMetadata.ControlConnection.Host;
+ return TaskHelper.Completed;
}
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
return HostDistance.Local;
}
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
- return _cluster.AllHosts();
+ return metadata.AllHosts();
}
}
}
diff --git a/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs b/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs
index 3cc51c4dd..0ddc1b520 100644
--- a/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs
+++ b/src/Cassandra.IntegrationTests/Core/PoolShortTests.cs
@@ -67,7 +67,7 @@ public void StopForce_With_Inflight_Requests(bool useStreamMode)
Assert.AreEqual(2, t.Result.Length, "The 2 hosts must have been used");
// Wait for all connections to be opened
Thread.Sleep(1000);
- var hosts = cluster.AllHosts().ToArray();
+ var hosts = cluster.Metadata.AllHosts().ToArray();
TestHelper.WaitUntil(() =>
hosts.Sum(h => session
.GetOrCreateConnectionPool(h, HostDistance.Local)
@@ -150,7 +150,7 @@ public async Task MarkHostDown_PartialPoolConnection()
using (var cluster = builder.Build())
{
var session = (IInternalSession)cluster.Connect();
- var allHosts = cluster.AllHosts();
+ var allHosts = cluster.Metadata.AllHosts();
TestHelper.WaitUntil(() =>
allHosts.Sum(h => session
@@ -256,7 +256,7 @@ public void Connect_With_Ssl_Test()
[TestCase(ProtocolVersion.V2, 2)]
public async Task PoolingOptions_Create_Based_On_Protocol(ProtocolVersion protocolVersion, int coreConnectionLength)
{
- var options1 = PoolingOptions.Get(protocolVersion);
+ var options1 = PoolingOptions.Create(protocolVersion);
using (var sCluster = SimulacronCluster.CreateNew(new SimulacronOptions()))
using (var cluster = ClusterBuilder()
.AddContactPoint(sCluster.InitialContactPoint)
@@ -264,7 +264,7 @@ public async Task PoolingOptions_Create_Based_On_Protocol(ProtocolVersion protoc
.Build())
{
var session = (IInternalSession)cluster.Connect();
- var allHosts = cluster.AllHosts();
+ var allHosts = cluster.Metadata.AllHosts();
var host = allHosts.First();
var pool = session.GetOrCreateConnectionPool(host, HostDistance.Local);
@@ -284,7 +284,7 @@ await TestHelper.RetryAssertAsync(async () =>
public async Task Should_Create_Core_Connections_To_Hosts_In_Local_Dc_When_Warmup_Is_Enabled()
{
const int nodeLength = 4;
- var poolingOptions = PoolingOptions.Get().SetCoreConnectionsPerHost(HostDistance.Local, 5);
+ var poolingOptions = PoolingOptions.Create().SetCoreConnectionsPerHost(HostDistance.Local, 5);
// Use multiple DCs: 4 nodes in first DC and 3 nodes in second DC
using (var testCluster = SimulacronCluster.CreateNew(new SimulacronOptions { Nodes = $"{nodeLength},3" }))
@@ -319,7 +319,7 @@ public async Task ControlConnection_Should_Reconnect_To_Up_Host()
using (var cluster = builder.AddContactPoint(testCluster.InitialContactPoint).Build())
{
var session = (Session)cluster.Connect();
- var allHosts = cluster.AllHosts();
+ var allHosts = cluster.Metadata.AllHosts();
Assert.AreEqual(3, allHosts.Count);
await TestHelper.TimesLimit(() =>
session.ExecuteAsync(new SimpleStatement("SELECT * FROM system.local")), 100, 16).ConfigureAwait(false);
@@ -330,27 +330,28 @@ await TestHelper.RetryAssertAsync(async () =>
Assert.AreEqual(4, (await testCluster.GetConnectedPortsAsync().ConfigureAwait(false)).Count);
}, 100, 200).ConfigureAwait(false);
- var ccAddress = cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback();
+ var cc = cluster.InternalRef.InternalMetadata.ControlConnection;
+ var ccAddress = cc.EndPoint.GetHostIpEndPointWithFallback();
Assert.NotNull(ccAddress);
var simulacronNode = testCluster.GetNode(ccAddress);
// Disable new connections to the first host
await simulacronNode.Stop().ConfigureAwait(false);
- TestHelper.WaitUntil(() => !cluster.GetHost(ccAddress).IsUp);
+ TestHelper.WaitUntil(() => !cluster.Metadata.GetHost(ccAddress).IsUp);
- Assert.False(cluster.GetHost(ccAddress).IsUp);
+ Assert.False(cluster.Metadata.GetHost(ccAddress).IsUp);
- TestHelper.WaitUntil(() => !cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback().Address.Equals(ccAddress.Address));
- Assert.NotNull(cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback());
- Assert.AreNotEqual(ccAddress.Address, cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback().Address);
+ TestHelper.WaitUntil(() => !cc.EndPoint.GetHostIpEndPointWithFallback().Address.Equals(ccAddress.Address));
+ Assert.NotNull(cc.EndPoint.GetHostIpEndPointWithFallback());
+ Assert.AreNotEqual(ccAddress.Address, cc.EndPoint.GetHostIpEndPointWithFallback().Address);
// Previous host is still DOWN
- Assert.False(cluster.GetHost(ccAddress).IsUp);
+ Assert.False(cluster.Metadata.GetHost(ccAddress).IsUp);
// New host is UP
- ccAddress = cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback();
- Assert.True(cluster.GetHost(ccAddress).IsUp);
+ ccAddress = cc.EndPoint.GetHostIpEndPointWithFallback();
+ Assert.True(cluster.Metadata.GetHost(ccAddress).IsUp);
}
}
@@ -368,7 +369,7 @@ public async Task ControlConnection_Should_Reconnect_After_Failed_Attemps()
using (var cluster = builder.AddContactPoint(testCluster.InitialContactPoint).Build())
{
var session = (Session)cluster.Connect();
- var allHosts = cluster.AllHosts();
+ var allHosts = cluster.Metadata.AllHosts();
Assert.AreEqual(3, allHosts.Count);
await TestHelper.TimesLimit(() =>
session.ExecuteAsync(new SimpleStatement("SELECT * FROM system.local")), 100, 16).ConfigureAwait(false);
@@ -385,7 +386,7 @@ await TestHelper.RetryAssertAsync(async () =>
// Disable all connections
await testCluster.DisableConnectionListener().ConfigureAwait(false);
- var ccAddress = cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback();
+ var ccAddress = cluster.InternalRef.InternalMetadata.ControlConnection.EndPoint.GetHostIpEndPointWithFallback();
// Drop all connections to hosts
foreach (var connection in serverConnections)
@@ -393,20 +394,20 @@ await TestHelper.RetryAssertAsync(async () =>
await testCluster.DropConnection(connection).ConfigureAwait(false);
}
- TestHelper.WaitUntil(() => !cluster.GetHost(ccAddress).IsUp);
+ TestHelper.WaitUntil(() => !cluster.Metadata.GetHost(ccAddress).IsUp);
// All host should be down by now
- TestHelper.WaitUntil(() => cluster.AllHosts().All(h => !h.IsUp));
+ TestHelper.WaitUntil(() => cluster.Metadata.AllHosts().All(h => !h.IsUp));
- Assert.False(cluster.GetHost(ccAddress).IsUp);
+ Assert.False(cluster.Metadata.GetHost(ccAddress).IsUp);
// Allow new connections to be created
await testCluster.EnableConnectionListener().ConfigureAwait(false);
- TestHelper.WaitUntil(() => cluster.AllHosts().All(h => h.IsUp));
+ TestHelper.WaitUntil(() => cluster.Metadata.AllHosts().All(h => h.IsUp));
- ccAddress = cluster.InternalRef.GetControlConnection().EndPoint.GetHostIpEndPointWithFallback();
- Assert.True(cluster.GetHost(ccAddress).IsUp);
+ ccAddress = cluster.InternalRef.InternalMetadata.ControlConnection.EndPoint.GetHostIpEndPointWithFallback();
+ Assert.True(cluster.Metadata.GetHost(ccAddress).IsUp);
// Once all connections are created, the control connection should be usable
await TestHelper.RetryAssertAsync(async () =>
@@ -418,7 +419,7 @@ await TestHelper.RetryAssertAsync(async () =>
TestHelper.RetryAssert(() =>
{
- Assert.DoesNotThrowAsync(() => cluster.InternalRef.GetControlConnection().QueryAsync("SELECT * FROM system.local"));
+ Assert.DoesNotThrowAsync(() => cluster.InternalRef.InternalMetadata.ControlConnection.QueryAsync("SELECT * FROM system.local"));
}, 100, 100);
}
}
@@ -430,7 +431,7 @@ public async Task Should_Use_Next_Host_When_First_Host_Is_Busy()
const int maxRequestsPerConnection = 100;
var builder = ClusterBuilder()
.WithPoolingOptions(
- PoolingOptions.Get()
+ PoolingOptions.Create()
.SetCoreConnectionsPerHost(HostDistance.Local, connectionLength)
.SetMaxConnectionsPerHost(HostDistance.Local, connectionLength)
.SetHeartBeatInterval(0)
@@ -443,7 +444,7 @@ public async Task Should_Use_Next_Host_When_First_Host_Is_Busy()
testCluster.PrimeFluent(b => b.WhenQuery(query).ThenVoidSuccess().WithDelayInMs(3000));
var session = await cluster.ConnectAsync().ConfigureAwait(false);
- var hosts = cluster.AllHosts().ToArray();
+ var hosts = cluster.Metadata.AllHosts().ToArray();
// Wait until all connections to first host are created
await TestHelper.WaitUntilAsync(() =>
@@ -480,7 +481,7 @@ public async Task Should_Throw_NoHostAvailableException_When_All_Host_Are_Busy()
var builder = ClusterBuilder()
.WithPoolingOptions(
- PoolingOptions.Get()
+ PoolingOptions.Create()
.SetCoreConnectionsPerHost(HostDistance.Local, connectionLength)
.SetMaxConnectionsPerHost(HostDistance.Local, connectionLength)
.SetHeartBeatInterval(0)
@@ -495,7 +496,7 @@ public async Task Should_Throw_NoHostAvailableException_When_All_Host_Are_Busy()
testCluster.PrimeFluent(b => b.WhenQuery(query).ThenVoidSuccess().WithDelayInMs(3000));
var session = await cluster.ConnectAsync().ConfigureAwait(false);
- var hosts = cluster.AllHosts().ToArray();
+ var hosts = cluster.Metadata.AllHosts().ToArray();
await TestHelper.TimesLimit(() =>
session.ExecuteAsync(new SimpleStatement("SELECT key FROM system.local")), 100, 16).ConfigureAwait(false);
@@ -562,8 +563,8 @@ public async Task Should_Use_Single_Host_When_Configured_At_Statement_Level()
using (var cluster = builder.AddContactPoint(testCluster.InitialContactPoint).Build())
{
var session = await cluster.ConnectAsync().ConfigureAwait(false);
- var firstHost = cluster.AllHosts().First();
- var lastHost = cluster.AllHosts().Last();
+ var firstHost = cluster.Metadata.AllHosts().First();
+ var lastHost = cluster.Metadata.AllHosts().Last();
// The test load-balancing policy targets always the first host
await TestHelper.TimesLimit(async () =>
@@ -599,7 +600,7 @@ public void Should_Throw_NoHostAvailableException_When_Targeting_Single_Ignored_
using (var cluster = builder.AddContactPoint(testCluster.InitialContactPoint).Build())
{
var session = cluster.Connect();
- var lastHost = cluster.AllHosts().Last();
+ var lastHost = cluster.Metadata.AllHosts().Last();
// Use the last host
var statement = new SimpleStatement(query).SetHost(lastHost);
@@ -620,7 +621,7 @@ public async Task Should_Throw_NoHostAvailableException_When_Targeting_Single_Ho
using (var cluster = builder.AddContactPoint(testCluster.InitialContactPoint).Build())
{
var session = await cluster.ConnectAsync().ConfigureAwait(false);
- var lastHost = cluster.AllHosts().Last();
+ var lastHost = cluster.Metadata.AllHosts().Last();
// 1 for the control connection and 1 connection per each host
await TestHelper.RetryAssertAsync(async () =>
@@ -650,7 +651,7 @@ await TestHelper.RetryAssertAsync(async () =>
TestHelper.RetryAssert(() =>
{
- var openConnections = session.GetState().GetOpenConnections(session.Cluster.GetHost(simulacronNode.IpEndPoint));
+ var openConnections = session.GetState().GetOpenConnections(session.Cluster.Metadata.GetHost(simulacronNode.IpEndPoint));
Assert.AreEqual(0, openConnections);
}, 100, 200);
@@ -684,7 +685,7 @@ public void The_Query_Plan_Should_Contain_A_Single_Host_When_Targeting_Single_Ho
testCluster.PrimeFluent(b => b.WhenQuery(query).ThenOverloaded("Test overloaded error"));
var session = cluster.Connect();
- var host = cluster.AllHosts().Last();
+ var host = cluster.Metadata.AllHosts().Last();
var statement = new SimpleStatement(query).SetHost(host).SetIdempotence(true);
@@ -714,7 +715,7 @@ public async Task Should_Not_Use_The_LoadBalancingPolicy_When_Targeting_Single_H
using (var cluster = builder.AddContactPoint(testCluster.InitialContactPoint).Build())
{
var session = await cluster.ConnectAsync().ConfigureAwait(false);
- var host = cluster.AllHosts().Last();
+ var host = cluster.Metadata.AllHosts().Last();
Interlocked.Exchange(ref queryPlanCounter, 0);
await TestHelper.TimesLimit(() =>
diff --git a/src/Cassandra.IntegrationTests/Linq/LinqTable/CreateTable.cs b/src/Cassandra.IntegrationTests/Linq/LinqTable/CreateTable.cs
index 7307f4f5b..16066b198 100644
--- a/src/Cassandra.IntegrationTests/Linq/LinqTable/CreateTable.cs
+++ b/src/Cassandra.IntegrationTests/Linq/LinqTable/CreateTable.cs
@@ -546,7 +546,7 @@ public void CreateTable_With_Frozen_Tuple()
new StubTableColumn("t", StubColumnKind.Regular, DataType.Frozen(DataType.Tuple(DataType.BigInt, DataType.BigInt, DataType.Text)))
});
- SessionCluster.RefreshSchema(_uniqueKsName, "tbl_frozen_tuple");
+ SessionCluster.Metadata.RefreshSchema(_uniqueKsName, "tbl_frozen_tuple");
var tableMeta = SessionCluster.Metadata.GetTable(_uniqueKsName, "tbl_frozen_tuple");
Assert.NotNull(tableMeta);
@@ -590,7 +590,7 @@ public void CreateTable_With_Counter_Static()
new StubTableColumn("id2", StubColumnKind.ClusteringKey, DataType.Text)
});
- SessionCluster.RefreshSchema(_uniqueKsName, "tbl_with_counter_static");
+ SessionCluster.Metadata.RefreshSchema(_uniqueKsName, "tbl_with_counter_static");
var tableMeta = SessionCluster.Metadata.GetTable(_uniqueKsName, "tbl_with_counter_static");
Assert.AreEqual(4, tableMeta.TableColumns.Length);
diff --git a/src/Cassandra.Tests/BaseUnitTest.cs b/src/Cassandra.Tests/BaseUnitTest.cs
index fed7c9b55..1311d6fe0 100644
--- a/src/Cassandra.Tests/BaseUnitTest.cs
+++ b/src/Cassandra.Tests/BaseUnitTest.cs
@@ -18,6 +18,7 @@
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+using Cassandra.Tasks;
using NUnit.Framework;
namespace Cassandra.Tests
@@ -69,17 +70,17 @@ public TestLoadBalancingPolicy(HostDistance distance = HostDistance.Local)
_distance = distance;
}
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
-
+ return TaskHelper.Completed;
}
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
return _distance;
}
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
return new[]
{
diff --git a/src/Cassandra.Tests/BuilderTests.cs b/src/Cassandra.Tests/BuilderTests.cs
index 0fb48e36d..5691e520f 100644
--- a/src/Cassandra.Tests/BuilderTests.cs
+++ b/src/Cassandra.Tests/BuilderTests.cs
@@ -146,7 +146,7 @@ public void MaxProtocolVersion_Defaults_To_Cluster_Max()
var builder = Cluster.Builder()
.AddContactPoint("192.168.1.10");
var cluster = builder.Build();
- Assert.AreEqual(Cluster.MaxProtocolVersion, cluster.Configuration.ProtocolOptions.MaxProtocolVersion);
+ Assert.AreEqual(Configuration.MaxProtocolVersion, cluster.Configuration.ProtocolOptions.MaxProtocolVersion);
//Defaults to null
Assert.Null(new ProtocolOptions().MaxProtocolVersion);
}
diff --git a/src/Cassandra.Tests/ClusterTests.cs b/src/Cassandra.Tests/ClusterTests.cs
index 5fb203b35..ed4f7e129 100644
--- a/src/Cassandra.Tests/ClusterTests.cs
+++ b/src/Cassandra.Tests/ClusterTests.cs
@@ -19,7 +19,9 @@
using System.Diagnostics;
using System.Linq;
using System.Net;
+using System.Threading.Tasks;
using Cassandra.ExecutionProfiles;
+using Cassandra.Tasks;
using Cassandra.Tests.Connections.TestHelpers;
using Moq;
using NUnit.Framework;
@@ -76,7 +78,7 @@ public void ClusterAllHostsReturnsZeroHostsOnDisconnectedCluster()
.AddContactPoint(ip)
.Build();
//No ring was discovered
- Assert.AreEqual(0, cluster.AllHosts().Count);
+ Assert.AreEqual(0, cluster.Metadata.AllHosts().Count);
}
[Test]
@@ -233,7 +235,7 @@ as IExecutionProfile
cluster.Connect();
cluster.Dispose();
- foreach (var h in cluster.AllHosts())
+ foreach (var h in cluster.Metadata.AllHosts())
{
Assert.AreEqual(expected[h.Address.Address.ToString()], h.GetDistanceUnsafe());
}
@@ -242,26 +244,25 @@ as IExecutionProfile
internal class FakeHostDistanceLbp : ILoadBalancingPolicy
{
private readonly IDictionary _distances;
- private ICluster _cluster;
public FakeHostDistanceLbp(IDictionary distances)
{
_distances = distances;
}
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
- _cluster = cluster;
+ return TaskHelper.Completed;
}
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
return _distances[host.Address.Address.ToString()];
}
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
- return _cluster.AllHosts().OrderBy(h => Guid.NewGuid().GetHashCode()).Take(_distances.Count);
+ return metadata.AllHosts().OrderBy(h => Guid.NewGuid().GetHashCode()).Take(_distances.Count);
}
}
}
diff --git a/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs b/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs
index db3b8f8a6..4714851eb 100644
--- a/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs
+++ b/src/Cassandra.Tests/Connections/Control/ControlConnectionTests.cs
@@ -24,6 +24,7 @@
using Cassandra.Connections;
using Cassandra.Connections.Control;
using Cassandra.ProtocolEvents;
+using Cassandra.Serialization;
using Cassandra.SessionManagement;
using Cassandra.Tests.Connections.TestHelpers;
using Cassandra.Tests.MetadataHelpers.TestHelpers;
@@ -103,10 +104,17 @@ private ControlConnectionCreateResult NewInstance(
}
Mock.Get(cluster).SetupGet(c => c.Configuration).Returns(config);
-
+ var contactPoints = new List
+ {
+ new IpLiteralContactPoint(
+ IPAddress.Parse("127.0.0.1"),
+ config.ProtocolOptions,
+ config.ServerNameResolver)
+ };
+
if (metadata == null)
{
- metadata = new Metadata(config);
+ metadata = new Metadata(cluster, config, config.SerializerManager, contactPoints);
}
return new ControlConnectionCreateResult
@@ -115,19 +123,7 @@ private ControlConnectionCreateResult NewInstance(
Metadata = metadata,
Cluster = cluster,
Config = config,
- ControlConnection = new ControlConnection(
- cluster,
- GetEventDebouncer(config),
- ProtocolVersion.MaxSupported,
- config,
- metadata,
- new List
- {
- new IpLiteralContactPoint(
- IPAddress.Parse("127.0.0.1"),
- config.ProtocolOptions,
- config.ServerNameResolver)
- })
+ ControlConnection = (ControlConnection)metadata.InternalMetadata.ControlConnection
};
}
@@ -211,9 +207,8 @@ public void Should_NotAttemptDownOrIgnoredHosts()
Mock.Get(cluster)
.Setup(c => c.RetrieveAndSetDistance(It.IsAny()))
- .Returns(h => config.Policies.LoadBalancingPolicy.Distance(h));
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(() => metadata.AllHosts());
- config.Policies.LoadBalancingPolicy.InitializeAsync(cluster);
+ .Returns(h => config.Policies.LoadBalancingPolicy.Distance(metadata, h));
+ config.Policies.LoadBalancingPolicy.InitializeAsync(metadata);
connectionOpenEnabled = false;
@@ -267,11 +262,9 @@ public async Task Should_NotLeakConnections_When_DisposeAndReconnectHappenSimult
Mock.Get(cluster)
.Setup(c => c.RetrieveAndSetDistance(It.IsAny()))
- .Returns(h => config.Policies.LoadBalancingPolicy.Distance(h));
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(() => metadata.AllHosts());
- Mock.Get(cluster).Setup(c => c.GetControlConnection()).Returns(cc);
- config.LocalDatacenterProvider.Initialize(cluster);
- config.Policies.LoadBalancingPolicy.InitializeAsync(cluster);
+ .Returns(h => config.Policies.LoadBalancingPolicy.Distance(metadata, h));
+ config.LocalDatacenterProvider.Initialize(cluster, metadata.InternalMetadata);
+ await config.Policies.LoadBalancingPolicy.InitializeAsync(metadata).ConfigureAwait(false);
createResult.ConnectionFactory.CreatedConnections.Clear();
@@ -343,7 +336,8 @@ private ControlConnectionCreateResult CreateForContactPointTest(bool keepContact
var config = new TestConfigurationBuilder
{
ConnectionFactory = connectionFactory,
- KeepContactPointsUnresolved = keepContactPointsUnresolved
+ KeepContactPointsUnresolved = keepContactPointsUnresolved,
+ SerializerManager = new SerializerManager(ProtocolVersion.V3, new TypeSerializerDefinitions().Definitions)
}.Build();
_cp1 = new TestContactPoint(new List
{
@@ -358,22 +352,21 @@ private ControlConnectionCreateResult CreateForContactPointTest(bool keepContact
new ConnectionEndPoint(_endpoint1, config.ServerNameResolver, _localhost),
new ConnectionEndPoint(_endpoint2, config.ServerNameResolver, _localhost)
});
+ config.SerializerManager.ChangeProtocolVersion(ProtocolVersion.V3);
+ var metadata = new Metadata(
+ Mock.Of(),
+ config,
+ config.SerializerManager,
+ new List
+ {
+ _cp1,
+ _cp2,
+ _localhost
+ });
return new ControlConnectionCreateResult
{
ConnectionFactory = connectionFactory,
- ControlConnection = new ControlConnection(
- Mock.Of(),
- new ProtocolEventDebouncer(
- new FakeTimerFactory(), TimeSpan.Zero, TimeSpan.Zero),
- ProtocolVersion.V3,
- config,
- new Metadata(config),
- new List
- {
- _cp1,
- _cp2,
- _localhost
- })
+ ControlConnection = (ControlConnection)metadata.InternalMetadata.ControlConnection
};
}
diff --git a/src/Cassandra.Tests/Connections/Control/TopologyRefresherTests.cs b/src/Cassandra.Tests/Connections/Control/TopologyRefresherTests.cs
index bbb831a48..17a42b456 100644
--- a/src/Cassandra.Tests/Connections/Control/TopologyRefresherTests.cs
+++ b/src/Cassandra.Tests/Connections/Control/TopologyRefresherTests.cs
@@ -23,6 +23,7 @@
using Cassandra.Connections.Control;
using Cassandra.Requests;
using Cassandra.Serialization;
+using Cassandra.SessionManagement;
using Cassandra.Tests.Connections.TestHelpers;
using Moq;
using NUnit.Framework;
@@ -36,7 +37,7 @@ public class TopologyRefresherTests
private const string PeersQuery = "SELECT * FROM system.peers";
private const string PeersV2Query = "SELECT * FROM system.peers_v2";
- private Metadata _metadata;
+ private IInternalMetadata _internalMetadata;
private ISerializer _serializer = new SerializerManager(ProtocolVersion.MaxSupported).GetCurrentSerializer();
@@ -102,9 +103,8 @@ private TopologyRefresher CreateTopologyRefresher(
{
MetadataRequestHandler = fakeRequestHandler
}.Build();
- var metadata = new Metadata(config);
- _metadata = metadata;
- return new TopologyRefresher(metadata, config);
+ _internalMetadata = new Metadata(Mock.Of(), config, config.SerializerManager, new List()).InternalMetadata;
+ return new TopologyRefresher(_internalMetadata, config);
}
[Test]
@@ -115,8 +115,8 @@ public async Task Should_SendSystemLocalAndPeersV1AndPeersV2Queries()
{
MetadataRequestHandler = fakeRequestHandler
}.Build();
- _metadata = new Metadata(config);
- var topologyRefresher = new TopologyRefresher(_metadata, config);
+ _internalMetadata = new Metadata(Mock.Of(), config, config.SerializerManager, new List()).InternalMetadata;
+ var topologyRefresher = new TopologyRefresher(_internalMetadata, config);
var connection = Mock.Of();
await topologyRefresher
@@ -139,8 +139,8 @@ public async Task Should_SendSystemLocalAndPeersV2Queries()
{
MetadataRequestHandler = fakeRequestHandler
}.Build();
- _metadata = new Metadata(config);
- var topologyRefresher = new TopologyRefresher(_metadata, config);
+ _internalMetadata = new Metadata(Mock.Of(), config, config.SerializerManager, new List()).InternalMetadata;
+ var topologyRefresher = new TopologyRefresher(_internalMetadata, config);
var connection = Mock.Of();
await topologyRefresher
@@ -161,8 +161,8 @@ public async Task Should_KeepSendingSystemPeersV2Queries_When_ItDoesNotFail()
{
MetadataRequestHandler = fakeRequestHandler
}.Build();
- _metadata = new Metadata(config);
- var topologyRefresher = new TopologyRefresher(_metadata, config);
+ _internalMetadata = new Metadata(Mock.Of(), config, config.SerializerManager, new List()).InternalMetadata;
+ var topologyRefresher = new TopologyRefresher(_internalMetadata, config);
var connection = Mock.Of();
await topologyRefresher
@@ -191,8 +191,8 @@ public async Task Should_SendPeersV1OnlyAfterPeersV2Fails()
{
var fakeRequestHandler = CreateFakeMetadataRequestHandler();
var config = new TestConfigurationBuilder { MetadataRequestHandler = fakeRequestHandler }.Build();
- _metadata = new Metadata(config);
- var topologyRefresher = new TopologyRefresher(_metadata, config);
+ _internalMetadata = new Metadata(Mock.Of(), config, config.SerializerManager, new List()).InternalMetadata;
+ var topologyRefresher = new TopologyRefresher(_internalMetadata, config);
var connection = Mock.Of();
await topologyRefresher
@@ -226,7 +226,7 @@ public async Task Should_SetClusterName()
await topologyRefresher.RefreshNodeListAsync(
new FakeConnectionEndPoint("127.0.0.1", 9042), connection, _serializer).ConfigureAwait(false);
- Assert.AreEqual("ut-cluster", _metadata.ClusterName);
+ Assert.AreEqual("ut-cluster", _internalMetadata.ClusterName);
}
[Test]
@@ -245,14 +245,14 @@ await topologyRefresher.RefreshNodeListAsync(
new FakeConnectionEndPoint("127.0.0.1", 9042), Mock.Of(), _serializer)
.ConfigureAwait(false);
- Assert.AreEqual(3, _metadata.AllHosts().Count);
+ Assert.AreEqual(3, _internalMetadata.AllHosts().Count);
//using rpc_address
- var host2 = _metadata.GetHost(new IPEndPoint(hostAddress2, ProtocolOptions.DefaultPort));
+ var host2 = _internalMetadata.GetHost(new IPEndPoint(hostAddress2, ProtocolOptions.DefaultPort));
Assert.NotNull(host2);
Assert.AreEqual("ut-dc2", host2.Datacenter);
Assert.AreEqual("ut-rack2", host2.Rack);
//with rpc_address = 0.0.0.0, use peer
- var host3 = _metadata.GetHost(new IPEndPoint(hostAddress3, ProtocolOptions.DefaultPort));
+ var host3 = _internalMetadata.GetHost(new IPEndPoint(hostAddress3, ProtocolOptions.DefaultPort));
Assert.NotNull(host3);
Assert.AreEqual("ut-dc3", host3.Datacenter);
Assert.AreEqual("ut-rack3", host3.Rack);
@@ -273,7 +273,7 @@ await topologyRefresher.RefreshNodeListAsync(
.ConfigureAwait(false);
//Only local host present
- Assert.AreEqual(1, _metadata.AllHosts().Count);
+ Assert.AreEqual(1, _internalMetadata.AllHosts().Count);
}
[Test]
@@ -285,9 +285,7 @@ public async Task UpdatePeersInfoUsesAddressTranslator()
.Setup(t => t.Translate(It.IsAny()))
.Callback(invokedEndPoints.Add)
.Returns(e => e);
- const int portNumber = 9999;
- var metadata = new Metadata(new Configuration());
- var hostAddress2 = IPAddress.Parse("127.0.0.2");
+ const int portNumber = 9999;var hostAddress2 = IPAddress.Parse("127.0.0.2");
var hostAddress3 = IPAddress.Parse("127.0.0.3");
var rows = TestHelper.CreateRows(new List>
{
@@ -303,13 +301,14 @@ public async Task UpdatePeersInfoUsesAddressTranslator()
StartupOptionsFactory = Mock.Of(),
MetadataRequestHandler = requestHandler
}.Build();
- var topologyRefresher = new TopologyRefresher(metadata, config);
+ _internalMetadata = new Metadata(Mock.Of(), config, config.SerializerManager, new List()).InternalMetadata;
+ var topologyRefresher = new TopologyRefresher(_internalMetadata, config);
await topologyRefresher.RefreshNodeListAsync(
new FakeConnectionEndPoint("127.0.0.1", 9042), Mock.Of(), _serializer)
.ConfigureAwait(false);
- Assert.AreEqual(3, metadata.AllHosts().Count);
+ Assert.AreEqual(3, _internalMetadata.AllHosts().Count);
Assert.AreEqual(2, invokedEndPoints.Count);
Assert.AreEqual(hostAddress2, invokedEndPoints[0].Address);
Assert.AreEqual(portNumber, invokedEndPoints[0].Port);
diff --git a/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs b/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs
index eccc8e26e..c8e521c57 100644
--- a/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs
+++ b/src/Cassandra.Tests/Connections/HostConnectionPoolTests.cs
@@ -104,7 +104,7 @@ private IHostConnectionPool CreatePool(IEndPointResolver res = null)
new DefaultRetryPolicy(),
NoSpeculativeExecutionPolicy.Instance,
new AtomicMonotonicTimestampGenerator()),
- PoolingOptions = PoolingOptions.Get(ProtocolVersion.V4).SetCoreConnectionsPerHost(HostDistance.Local, 2)
+ PoolingOptions = PoolingOptions.Create(ProtocolVersion.V4).SetCoreConnectionsPerHost(HostDistance.Local, 2)
}.Build();
var pool = new HostConnectionPool(
diff --git a/src/Cassandra.Tests/Connections/TestHelpers/FakeControlConnectionFactory.cs b/src/Cassandra.Tests/Connections/TestHelpers/FakeControlConnectionFactory.cs
index 65782d18a..7e3652322 100644
--- a/src/Cassandra.Tests/Connections/TestHelpers/FakeControlConnectionFactory.cs
+++ b/src/Cassandra.Tests/Connections/TestHelpers/FakeControlConnectionFactory.cs
@@ -18,11 +18,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+
using Cassandra.Connections;
using Cassandra.Connections.Control;
using Cassandra.ProtocolEvents;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
+
using Moq;
namespace Cassandra.Tests.Connections.TestHelpers
@@ -32,9 +34,9 @@ internal class FakeControlConnectionFactory : IControlConnectionFactory
public IControlConnection Create(
IInternalCluster cluster,
IProtocolEventDebouncer protocolEventDebouncer,
- ProtocolVersion initialProtocolVersion,
+ ISerializerManager serializerManager,
Configuration config,
- Metadata metadata,
+ IInternalMetadata metadata,
IEnumerable contactPoints)
{
var cc = Mock.Of();
@@ -54,8 +56,8 @@ public IControlConnection Create(
}
}
metadata.SetResolvedContactPoints(cps);
+ serializerManager.ChangeProtocolVersion(ProtocolVersion.V3);
}));
- Mock.Get(cc).Setup(c => c.Serializer).Returns(new SerializerManager(ProtocolVersion.V3));
return cc;
}
diff --git a/src/Cassandra.Tests/Connections/TestHelpers/FakeProtocolVersionNegotiator.cs b/src/Cassandra.Tests/Connections/TestHelpers/FakeProtocolVersionNegotiator.cs
index adbcaf74b..a0879221c 100644
--- a/src/Cassandra.Tests/Connections/TestHelpers/FakeProtocolVersionNegotiator.cs
+++ b/src/Cassandra.Tests/Connections/TestHelpers/FakeProtocolVersionNegotiator.cs
@@ -23,13 +23,19 @@ namespace Cassandra.Tests.Connections.TestHelpers
{
internal class FakeProtocolVersionNegotiator : IProtocolVersionNegotiator
{
- public Task ChangeProtocolVersion(Configuration config, ISerializerManager serializer, ProtocolVersion nextVersion, IConnection previousConnection,
- UnsupportedProtocolVersionException ex = null, ProtocolVersion? previousVersion = null)
+ public Task ChangeProtocolVersion(
+ Configuration config,
+ ISerializerManager serializer,
+ ProtocolVersion nextVersion,
+ IConnection previousConnection,
+ UnsupportedProtocolVersionException ex = null,
+ ProtocolVersion? previousVersion = null)
{
return Task.FromResult(previousConnection);
}
- public Task NegotiateVersionAsync(Configuration config, Metadata metadata, IConnection connection, ISerializerManager serializer)
+ public Task NegotiateVersionAsync(
+ Configuration config, IInternalMetadata internalMetadata, IConnection connection, ISerializerManager serializer)
{
return Task.FromResult(connection);
}
diff --git a/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializer.cs b/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializer.cs
index 981118fbc..e3ea1b664 100644
--- a/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializer.cs
+++ b/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializer.cs
@@ -22,11 +22,11 @@ namespace Cassandra.Tests.Connections.TestHelpers
{
internal class FakeSupportedOptionsInitializer : ISupportedOptionsInitializer
{
- private Metadata _metadata;
+ private IInternalMetadata _internalMetadata;
- public FakeSupportedOptionsInitializer(Metadata metadata)
+ public FakeSupportedOptionsInitializer(IInternalMetadata internalMetadata)
{
- _metadata = metadata;
+ _internalMetadata = internalMetadata;
}
public Task ApplySupportedOptionsAsync(IConnection connection)
diff --git a/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializerFactory.cs b/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializerFactory.cs
index 07efebb5c..e63973bba 100644
--- a/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializerFactory.cs
+++ b/src/Cassandra.Tests/Connections/TestHelpers/FakeSupportedOptionsInitializerFactory.cs
@@ -19,9 +19,9 @@ namespace Cassandra.Tests.Connections.TestHelpers
{
internal class FakeSupportedOptionsInitializerFactory : ISupportedOptionsInitializerFactory
{
- public ISupportedOptionsInitializer Create(Metadata metadata)
+ public ISupportedOptionsInitializer Create(IInternalMetadata internalMetadata)
{
- return new FakeSupportedOptionsInitializer(metadata);
+ return new FakeSupportedOptionsInitializer(internalMetadata);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresher.cs b/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresher.cs
index 421bb78e2..4f492b9d1 100644
--- a/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresher.cs
+++ b/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresher.cs
@@ -25,30 +25,30 @@ namespace Cassandra.Tests.Connections.TestHelpers
{
internal class FakeTopologyRefresher : ITopologyRefresher
{
- private readonly Metadata _metadata;
- private readonly Configuration _config;
+ private readonly IInternalMetadata _internalMetadata;
private readonly IDictionary _hosts;
- public FakeTopologyRefresher(Metadata metadata, Configuration config, IDictionary hosts)
+ public FakeTopologyRefresher(
+ IInternalMetadata internalMetadata, IDictionary hosts)
{
- _metadata = metadata;
- _config = config;
+ _internalMetadata = internalMetadata;
_hosts = hosts;
}
- public Task RefreshNodeListAsync(IConnectionEndPoint currentEndPoint, IConnection connection, ISerializer serializer)
+ public Task RefreshNodeListAsync(
+ IConnectionEndPoint currentEndPoint, IConnection connection, ISerializer serializer)
{
foreach (var h in _hosts)
{
- if (_metadata.GetHost(h.Key) == null)
+ if (_internalMetadata.GetHost(h.Key) == null)
{
- var host = _metadata.AddHost(h.Key);
+ var host = _internalMetadata.AddHost(h.Key);
host.SetInfo(h.Value);
}
}
- _metadata.Partitioner = "Murmur3Partitioner";
- return Task.FromResult(_metadata.Hosts.First());
+ _internalMetadata.Partitioner = "Murmur3Partitioner";
+ return Task.FromResult(_internalMetadata.Hosts.First());
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresherFactory.cs b/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresherFactory.cs
index eae0343e4..548f30e50 100644
--- a/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresherFactory.cs
+++ b/src/Cassandra.Tests/Connections/TestHelpers/FakeTopologyRefresherFactory.cs
@@ -53,9 +53,9 @@ public FakeTopologyRefresherFactory(ICollection hosts)
_rows = rows;
}
- public ITopologyRefresher Create(Metadata metadata, Configuration config)
+ public ITopologyRefresher Create(IInternalMetadata internalMetadata, Configuration config)
{
- return new FakeTopologyRefresher(metadata, config, _rows);
+ return new FakeTopologyRefresher(internalMetadata, _rows);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs b/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs
index 8d7da5be8..70e0d9bc2 100644
--- a/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs
+++ b/src/Cassandra.Tests/DataStax/Insights/InsightsClientTests.cs
@@ -71,14 +71,14 @@ public void Should_LogFiveTimes_When_ThereAreMoreThanFiveErrorsOnStartupMessageS
cc => cc.UnsafeSendQueryRequestAsync(
"CALL InsightsRpc.reportInsight(?)",
It.IsAny());
- Mock.Get(cluster.Metadata.ControlConnection).Setup(mockExpression).ReturnsAsync((Response)null);
+ Mock.Get(cluster.InternalMetadata.ControlConnection).Setup(mockExpression).ReturnsAsync((Response)null);
- target.InitializeAsync();
+ target.Initialize(cluster.InternalMetadata);
TestHelper.RetryAssert(
() =>
{
- Mock.Get(cluster.Metadata.ControlConnection).Verify(mockExpression, Times.AtLeast(10));
+ Mock.Get(cluster.InternalMetadata.ControlConnection).Verify(mockExpression, Times.AtLeast(10));
},
30);
@@ -104,7 +104,7 @@ public void Should_ResetErrorCounterForLogging_When_ThereSendMessageIsSuccessful
cc => cc.UnsafeSendQueryRequestAsync(
"CALL InsightsRpc.reportInsight(?)",
It.IsAny());
- Mock.Get(cluster.Metadata.ControlConnection)
+ Mock.Get(cluster.InternalMetadata.ControlConnection)
.SetupSequence(mockExpression)
.ReturnsAsync((Response)null)
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
@@ -113,12 +113,12 @@ public void Should_ResetErrorCounterForLogging_When_ThereSendMessageIsSuccessful
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.ReturnsAsync((Response)null);
- target.InitializeAsync();
+ target.Initialize(cluster.InternalMetadata);
TestHelper.RetryAssert(
() =>
{
- Mock.Get(cluster.Metadata.ControlConnection).Verify(mockExpression, Times.AtLeast(20));
+ Mock.Get(cluster.InternalMetadata.ControlConnection).Verify(mockExpression, Times.AtLeast(20));
},
30);
@@ -251,13 +251,13 @@ public void Should_InvokeRpcCallCorrectlyAndImmediately_When_SendStartupMessageI
using (var target = InsightsClientTests.GetInsightsClient(cluster, session))
{
var queryProtocolOptions = new ConcurrentQueue();
- Mock.Get(cluster.Metadata.ControlConnection).Setup(cc => cc.UnsafeSendQueryRequestAsync(
+ Mock.Get(cluster.InternalMetadata.ControlConnection).Setup(cc => cc.UnsafeSendQueryRequestAsync(
"CALL InsightsRpc.reportInsight(?)",
It.IsAny()))
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.Callback((query, opts) => { queryProtocolOptions.Enqueue(opts); });
- target.InitializeAsync();
+ target.Initialize(cluster.InternalMetadata);
TestHelper.RetryAssert(
() => { Assert.GreaterOrEqual(queryProtocolOptions.Count, 1); }, 10, 50);
@@ -375,13 +375,13 @@ public void Should_InvokeRpcCallCorrectlyAndImmediatelyWithExecutionProfiles_Whe
using (var target = InsightsClientTests.GetInsightsClient(cluster, session))
{
var queryProtocolOptions = new ConcurrentQueue();
- Mock.Get(cluster.Metadata.ControlConnection).Setup(cc => cc.UnsafeSendQueryRequestAsync(
+ Mock.Get(cluster.InternalMetadata.ControlConnection).Setup(cc => cc.UnsafeSendQueryRequestAsync(
"CALL InsightsRpc.reportInsight(?)",
It.IsAny()))
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.Callback((query, opts) => { queryProtocolOptions.Enqueue(opts); });
- target.InitializeAsync();
+ target.Initialize(cluster.InternalMetadata);
TestHelper.RetryAssert(
() => { Assert.GreaterOrEqual(queryProtocolOptions.Count, 1); }, 10, 50);
@@ -412,13 +412,13 @@ public void Should_InvokeRpcCallCorrectlyAndPeriodically_When_SendStatusMessageI
using (var target = InsightsClientTests.GetInsightsClient(cluster, session))
{
var queryProtocolOptions = new ConcurrentQueue();
- Mock.Get(cluster.Metadata.ControlConnection).Setup(cc => cc.UnsafeSendQueryRequestAsync(
+ Mock.Get(cluster.InternalMetadata.ControlConnection).Setup(cc => cc.UnsafeSendQueryRequestAsync(
"CALL InsightsRpc.reportInsight(?)",
It.IsAny()))
.ReturnsAsync(new FakeResultResponse(ResultResponse.ResultResponseKind.Void))
.Callback((query, opts) => { queryProtocolOptions.Enqueue(opts); });
- target.InitializeAsync();
+ target.Initialize(cluster.InternalMetadata);
TestHelper.RetryAssert(() => { Assert.GreaterOrEqual(queryProtocolOptions.Count, 5); }, 5, 400);
queryProtocolOptions.TryDequeue(out var result); // ignore startup message
@@ -434,14 +434,14 @@ private static InsightsClient GetInsightsClient(IInternalCluster cluster, IInter
var timestampGeneratorMock = Mock.Of();
var platformInfoMock = Mock.Of>();
- Mock.Get(hostnameInfoMock).Setup(m => m.GetInformation(cluster, session)).Returns("awesome_hostname");
- Mock.Get(driverInfoMock).Setup(m => m.GetInformation(cluster, session)).Returns(new DriverInfo
+ Mock.Get(hostnameInfoMock).Setup(m => m.GetInformation(cluster, session, cluster.InternalMetadata)).Returns("awesome_hostname");
+ Mock.Get(driverInfoMock).Setup(m => m.GetInformation(cluster, session, cluster.InternalMetadata)).Returns(new DriverInfo
{
DriverVersion = "1.1.2",
DriverName = "Driver Name"
});
Mock.Get(timestampGeneratorMock).Setup(m => m.GenerateTimestamp()).Returns(124219041);
- Mock.Get(platformInfoMock).Setup(m => m.GetInformation(cluster, session)).Returns(new InsightsPlatformInfo
+ Mock.Get(platformInfoMock).Setup(m => m.GetInformation(cluster, session, cluster.InternalMetadata)).Returns(new InsightsPlatformInfo
{
CentralProcessingUnits = new CentralProcessingUnitsInfo
{
@@ -507,8 +507,8 @@ private IInternalSession GetSession(IInternalCluster cluster)
{ host1, mockPool1 },
{ host2, mockPool2 }
};
- Mock.Get(cluster).Setup(m => m.GetHost(host1)).Returns(new Host(host1, contactPoint: null));
- Mock.Get(cluster).Setup(m => m.GetHost(host2)).Returns(new Host(host2, contactPoint: null));
+ Mock.Get(cluster.InternalMetadata).Setup(m => m.GetHost(host1)).Returns(new Host(host1, contactPoint: null));
+ Mock.Get(cluster.InternalMetadata).Setup(m => m.GetHost(host2)).Returns(new Host(host2, contactPoint: null));
Mock.Get(session).Setup(s => s.GetPools()).Returns(pools.ToArray());
Mock.Get(session).Setup(m => m.Cluster).Returns(cluster);
Mock.Get(session).SetupGet(m => m.InternalSessionId).Returns(Guid.Parse("E21EAB96-D91E-4790-80BD-1D5FB5472258"));
@@ -519,17 +519,16 @@ private IInternalCluster GetCluster(bool withProfiles, int eventDelayMillisecond
{
var cluster = Mock.Of();
var config = GetConfig(eventDelayMilliseconds, withProfiles);
- var metadata = new Metadata(config)
- {
- ControlConnection = Mock.Of()
- };
- Mock.Get(metadata.ControlConnection).SetupGet(cc => cc.ProtocolVersion).Returns(ProtocolVersion.V4);
- Mock.Get(metadata.ControlConnection).SetupGet(cc => cc.EndPoint)
+ var metadata = Mock.Of();
+ var controlConnection = Mock.Of();
+ Mock.Get(metadata).SetupGet(m => m.ControlConnection).Returns(controlConnection);
+ Mock.Get(metadata).SetupGet(cc => cc.ProtocolVersion).Returns(ProtocolVersion.V4);
+ Mock.Get(controlConnection).SetupGet(cc => cc.EndPoint)
.Returns(new ConnectionEndPoint(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 9011), config.ServerNameResolver, null));
- Mock.Get(metadata.ControlConnection).SetupGet(cc => cc.LocalAddress).Returns(new IPEndPoint(IPAddress.Parse("10.10.10.2"), 9015));
+ Mock.Get(controlConnection).SetupGet(cc => cc.LocalAddress).Returns(new IPEndPoint(IPAddress.Parse("10.10.10.2"), 9015));
var hostIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9042);
var hostIp2 = new IPEndPoint(IPAddress.Parse("127.0.0.2"), 9042);
- metadata.SetResolvedContactPoints(new Dictionary>
+ Mock.Get(metadata).SetupGet(cc => cc.ResolvedContactPoints).Returns(new Dictionary>
{
{
new HostnameContactPoint(
@@ -540,12 +539,14 @@ private IInternalCluster GetCluster(bool withProfiles, int eventDelayMillisecond
"localhost"),
new[] { new ConnectionEndPoint(hostIp, config.ServerNameResolver, null) } }
});
- metadata.AddHost(hostIp);
- metadata.AddHost(hostIp2);
- metadata.Hosts.ToCollection().First().Datacenter = "dc123";
+ var hosts = new Hosts();
+ hosts.Add(hostIp);
+ hosts.Add(hostIp2);
+ hosts.ToCollection().First().Datacenter = "dc123";
+ Mock.Get(metadata).SetupGet(m => m.Hosts).Returns(hosts);
+ Mock.Get(metadata).Setup(m => m.AllHosts()).Returns(hosts.ToCollection);
Mock.Get(cluster).SetupGet(m => m.Configuration).Returns(config);
- Mock.Get(cluster).SetupGet(m => m.Metadata).Returns(metadata);
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(metadata.AllHosts);
+ Mock.Get(cluster).SetupGet(m => m.InternalMetadata).Returns(metadata);
return cluster;
}
@@ -553,7 +554,7 @@ private Configuration GetConfig(int eventDelayMilliseconds, bool withProfiles)
{
var graphOptions = new GraphOptions().SetName("testGraphName").SetReadConsistencyLevel(ConsistencyLevel.All);
var supportVerifier = Mock.Of();
- Mock.Get(supportVerifier).Setup(m => m.SupportsInsights(It.IsAny())).Returns(true);
+ Mock.Get(supportVerifier).Setup(m => m.SupportsInsights(It.IsAny())).Returns(true);
return new TestConfigurationBuilder
{
Policies = new Cassandra.Policies(
diff --git a/src/Cassandra.Tests/DataStax/Insights/InsightsSupportVerifierTests.cs b/src/Cassandra.Tests/DataStax/Insights/InsightsSupportVerifierTests.cs
index 322e88e28..baa4b82fc 100644
--- a/src/Cassandra.Tests/DataStax/Insights/InsightsSupportVerifierTests.cs
+++ b/src/Cassandra.Tests/DataStax/Insights/InsightsSupportVerifierTests.cs
@@ -16,6 +16,7 @@
using System.Collections.Generic;
using System.Net;
+using Cassandra.Connections.Control;
using Cassandra.DataStax.Insights;
using Cassandra.SessionManagement;
using Moq;
@@ -29,166 +30,166 @@ public class InsightsSupportVerifierTests
[Test]
public void Should_ReturnFalse_When_OneNode_6_0_4_AndOneNode_6_0_5()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.4", "6.0.5"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.4", "6.0.5"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_OneNode_6_0_5_AndOneNode_6_0_4()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5", "6.0.4"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5", "6.0.4"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_OneNode_6_1_0_AndOneNode_6_0_5()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.1.0", "6.0.5"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.1.0", "6.0.5"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_TwoNodes_6_0_5()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5", "6.0.5"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5", "6.0.5"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_TwoNodes_6_0_4()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.4", "6.0.4"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.4", "6.0.4"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_OneNode5_1_12()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("5.1.12"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("5.1.12"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_OneNode5_1_13()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("5.1.13"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("5.1.13"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_OneNode5_2_0()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("5.2.0"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("5.2.0"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_OneNode6_0_5()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_OneNode6_0_5_alpha()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5-alpha"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.5-alpha"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_OneNode6_0_4()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.4"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.0.4"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnTrue_When_OneNode6_1_0()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("6.1.0"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("6.1.0"));
var target = new InsightsSupportVerifier();
- Assert.IsTrue(target.SupportsInsights(cluster));
+ Assert.IsTrue(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_OneNode5_0_99()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("5.0.99"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("5.0.99"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_OneNode5_0_0()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("5.0.0"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("5.0.0"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
[Test]
public void Should_ReturnFalse_When_OneNode4_8_0()
{
- var cluster = Mock.Of();
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(GetHosts("4.8.0"));
+ var internalMetadata = Mock.Of();
+ Mock.Get(internalMetadata).Setup(c => c.AllHosts()).Returns(GetHosts("4.8.0"));
var target = new InsightsSupportVerifier();
- Assert.IsFalse(target.SupportsInsights(cluster));
+ Assert.IsFalse(target.SupportsInsights(internalMetadata));
}
private ICollection GetHosts(params string[] dseVersions)
diff --git a/src/Cassandra.Tests/DataStax/Insights/MessageFactories/InsightsMessageFactoryTests.cs b/src/Cassandra.Tests/DataStax/Insights/MessageFactories/InsightsMessageFactoryTests.cs
index 1968ea2f1..cec6c7d89 100644
--- a/src/Cassandra.Tests/DataStax/Insights/MessageFactories/InsightsMessageFactoryTests.cs
+++ b/src/Cassandra.Tests/DataStax/Insights/MessageFactories/InsightsMessageFactoryTests.cs
@@ -39,7 +39,7 @@ public void Should_ReturnCorrectMetadata_When_CreateStartupMessageIsCalled()
var target = Configuration.DefaultInsightsStartupMessageFactory;
var timestamp = (long)(DateTimeOffset.UtcNow - new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero)).TotalMilliseconds;
- var act = target.CreateMessage(cluster, Mock.Of());
+ var act = target.CreateMessage(cluster, Mock.Of(), cluster.InternalMetadata);
Assert.AreEqual(InsightType.Event, act.Metadata.InsightType);
Assert.AreEqual("v1", act.Metadata.InsightMappingId);
@@ -56,7 +56,7 @@ public void Should_ReturnCorrectMetadata_When_CreateStatusMessageIsCalled()
var target = Configuration.DefaultInsightsStatusMessageFactory;
var timestamp = (long)(DateTimeOffset.UtcNow - new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero)).TotalMilliseconds;
- var act = target.CreateMessage(cluster, Mock.Of());
+ var act = target.CreateMessage(cluster, Mock.Of(), cluster.InternalMetadata);
Assert.AreEqual(InsightType.Event, act.Metadata.InsightType);
Assert.AreEqual("v1", act.Metadata.InsightMappingId);
@@ -84,14 +84,14 @@ public void Should_ReturnCorrectData_When_CreateStatusMessageIsCalled()
{ host1, mockPool1 },
{ host2, mockPool2 }
};
- Mock.Get(cluster).Setup(m => m.GetHost(host1)).Returns(new Host(host1, contactPoint: null));
- Mock.Get(cluster).Setup(m => m.GetHost(host2)).Returns(new Host(host2, contactPoint: null));
+ Mock.Get(cluster.InternalMetadata).Setup(m => m.GetHost(host1)).Returns(new Host(host1, contactPoint: null));
+ Mock.Get(cluster.InternalMetadata).Setup(m => m.GetHost(host2)).Returns(new Host(host2, contactPoint: null));
Mock.Get(session).Setup(s => s.GetPools()).Returns(pools.ToArray());
Mock.Get(session).Setup(m => m.Cluster).Returns(cluster);
Mock.Get(session).SetupGet(m => m.InternalSessionId).Returns(Guid.Parse("E21EAB96-D91E-4790-80BD-1D5FB5472258"));
var target = Configuration.DefaultInsightsStatusMessageFactory;
- var act = target.CreateMessage(cluster, session);
+ var act = target.CreateMessage(cluster, session, cluster.InternalMetadata);
Assert.AreEqual("127.0.0.1:9011", act.Data.ControlConnection);
Assert.AreEqual("BECFE098-E462-47E7-B6A7-A21CD316D4C0", act.Data.ClientId.ToUpper());
@@ -111,7 +111,7 @@ public void Should_ReturnCorrectData_When_CreateStartupMessageIsCalled()
var session = Mock.Of();
Mock.Get(session).SetupGet(m => m.InternalSessionId).Returns(Guid.Parse("E21EAB96-D91E-4790-80BD-1D5FB5472258"));
- var act = target.CreateMessage(cluster, session);
+ var act = target.CreateMessage(cluster, session, cluster.InternalMetadata);
InsightsMessageFactoryTests.AssertStartupOptions(act);
@@ -226,11 +226,11 @@ private static void AssertExecutionProfile(Insight act)
private IInternalCluster GetCluster()
{
var cluster = Mock.Of();
+ var metadata = Mock.Of();
+ var ccMock = Mock.Of();
+ Mock.Get(cluster).SetupGet(c => c.InternalMetadata).Returns(metadata);
+ Mock.Get(metadata).SetupGet(m => m.ControlConnection).Returns(ccMock);
var config = GetConfig();
- var metadata = new Metadata(config)
- {
- ControlConnection = Mock.Of()
- };
var contactPoint = new HostnameContactPoint(
config.DnsResolver,
config.ProtocolOptions,
@@ -239,19 +239,20 @@ private IInternalCluster GetCluster()
"localhost");
var connectionEndPoint = new ConnectionEndPoint(
new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9011), config.ServerNameResolver, contactPoint);
- Mock.Get(metadata.ControlConnection).SetupGet(cc => cc.ProtocolVersion).Returns(ProtocolVersion.V4);
+ Mock.Get(metadata).SetupGet(m => m.ResolvedContactPoints).Returns(
+ new Dictionary>
+ {
+ { contactPoint, new[] { connectionEndPoint } }
+ });
+ Mock.Get(metadata).SetupGet(cc => cc.ProtocolVersion).Returns(ProtocolVersion.V4);
Mock.Get(metadata.ControlConnection).SetupGet(cc => cc.EndPoint).Returns(connectionEndPoint);
Mock.Get(metadata.ControlConnection).SetupGet(cc => cc.LocalAddress).Returns(new IPEndPoint(IPAddress.Parse("10.10.10.2"), 9015));
var hostIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9042);
- metadata.SetResolvedContactPoints(new Dictionary>
- {
- { contactPoint, new [] { connectionEndPoint } }
- });
- metadata.AddHost(hostIp);
- metadata.Hosts.ToCollection().First().Datacenter = "dc123";
+ var hosts = new Hosts();
+ hosts.Add(hostIp);
+ hosts.ToCollection().First().Datacenter = "dc123";
Mock.Get(cluster).SetupGet(m => m.Configuration).Returns(config);
- Mock.Get(cluster).SetupGet(m => m.Metadata).Returns(metadata);
- Mock.Get(cluster).Setup(c => c.AllHosts()).Returns(metadata.AllHosts);
+ Mock.Get(metadata).Setup(c => c.AllHosts()).Returns(hosts.ToCollection());
return cluster;
}
diff --git a/src/Cassandra.Tests/ExecutionProfiles/ClusterTests.cs b/src/Cassandra.Tests/ExecutionProfiles/ClusterTests.cs
index 0319ed4ae..137fb7ded 100644
--- a/src/Cassandra.Tests/ExecutionProfiles/ClusterTests.cs
+++ b/src/Cassandra.Tests/ExecutionProfiles/ClusterTests.cs
@@ -17,7 +17,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
+using System.Threading.Tasks;
using Cassandra.ExecutionProfiles;
+using Cassandra.Tasks;
using Cassandra.Tests.Connections.TestHelpers;
using Moq;
using NUnit.Framework;
@@ -324,17 +326,19 @@ private class FakeSpeculativeExecutionPolicy : ISpeculativeExecutionPolicy
public volatile int InitializeCount;
public volatile int DisposeCount;
- public void Dispose()
+ public Task ShutdownAsync()
{
DisposeCount++;
+ return TaskHelper.Completed;
}
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
InitializeCount++;
+ return TaskHelper.Completed;
}
- public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
+ public ISpeculativeExecutionPlan NewPlan(IMetadata metadata, string keyspace, IStatement statement)
{
throw new System.NotImplementedException();
}
@@ -343,22 +347,21 @@ public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
internal class FakeLoadBalancingPolicy : ILoadBalancingPolicy
{
public volatile int InitializeCount;
- private ICluster _cluster;
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
- _cluster = cluster;
InitializeCount++;
+ return TaskHelper.Completed;
}
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
return HostDistance.Local;
}
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
- return _cluster.AllHosts();
+ return metadata.AllHosts();
}
}
}
diff --git a/src/Cassandra.Tests/ExecutionProfiles/RequestHandlerTests.cs b/src/Cassandra.Tests/ExecutionProfiles/RequestHandlerTests.cs
index 72a4b48f2..3725ecfd5 100644
--- a/src/Cassandra.Tests/ExecutionProfiles/RequestHandlerTests.cs
+++ b/src/Cassandra.Tests/ExecutionProfiles/RequestHandlerTests.cs
@@ -29,6 +29,7 @@
using Cassandra.Responses;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
+using Cassandra.Tasks;
using Cassandra.Tests.Connections.TestHelpers;
using Cassandra.Tests.Requests;
@@ -279,14 +280,16 @@ private RequestHandlerMockResult BuildRequestHandler(
cluster.Connect();
// create session
- var session = new Session(cluster, config, null, SerializerManager.Default, null);
+ var session = new Session(cluster, config, null, null);
// create request handler
var options = profile != null
- ? new RequestOptions(profile, null, config.Policies, config.SocketOptions, config.QueryOptions, config.ClientOptions)
+ ? new RequestOptions(
+ profile, null, config.Policies, config.SocketOptions, config.QueryOptions, config.ClientOptions)
: config.DefaultRequestOptions;
var requestHandler = new RequestHandler(
session,
+ cluster.InternalRef.InternalMetadata,
new SerializerManager(ProtocolVersion.V3).GetCurrentSerializer(),
statement,
options);
@@ -368,16 +371,17 @@ private class FakeLoadBalancingPolicy : ILoadBalancingPolicy
{
public long Count;
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
+ return TaskHelper.Completed;
}
- public HostDistance Distance(Host host)
+ public HostDistance Distance(IMetadata metadata, Host host)
{
return HostDistance.Local;
}
- public IEnumerable NewQueryPlan(string keyspace, IStatement query)
+ public IEnumerable NewQueryPlan(IMetadata metadata, string keyspace, IStatement query)
{
Interlocked.Increment(ref Count);
return new List
@@ -449,18 +453,20 @@ private class FakeSpeculativeExecutionPolicy : ISpeculativeExecutionPolicy
{
public long Count;
- public void Dispose()
+ public Task ShutdownAsync()
{
+ return TaskHelper.Completed;
}
- public void Initialize(ICluster cluster)
+ public Task InitializeAsync(IMetadata metadata)
{
+ return TaskHelper.Completed;
}
- public ISpeculativeExecutionPlan NewPlan(string keyspace, IStatement statement)
+ public ISpeculativeExecutionPlan NewPlan(IMetadata metadata, string keyspace, IStatement statement)
{
Interlocked.Increment(ref Count);
- return new ConstantSpeculativeExecutionPolicy(10, 1).NewPlan(keyspace, statement);
+ return new ConstantSpeculativeExecutionPolicy(10, 1).NewPlan(metadata, keyspace, statement);
}
}
}
diff --git a/src/Cassandra.Tests/ExecutionProfiles/SessionTests.cs b/src/Cassandra.Tests/ExecutionProfiles/SessionTests.cs
index ccb21c3d3..e313c21e0 100644
--- a/src/Cassandra.Tests/ExecutionProfiles/SessionTests.cs
+++ b/src/Cassandra.Tests/ExecutionProfiles/SessionTests.cs
@@ -58,10 +58,10 @@ public async Task Should_CreateRequestHandlerWithCorrectRequestOptions_When_Exec
Mock.Get(clusterMock).Setup(c => c.Configuration).Returns(config);
Mock.Get(requestHandlerMock).Setup(r => r.SendAsync()).Returns(Task.FromResult(new RowSet()));
- var session = new Session(clusterMock, config, null, serializer, null);
+ var session = new Session(clusterMock, config, null, null);
Mock.Get(requestHandlerFactoryMock)
- .Setup(m => m.Create(session, serializer.GetCurrentSerializer(), It.IsAny(), config.RequestOptions["testE"]))
+ .Setup(m => m.Create(session, clusterMock.InternalMetadata, serializer.GetCurrentSerializer(), It.IsAny(), config.RequestOptions["testE"]))
.Returns(requestHandlerMock);
if (async)
@@ -73,7 +73,7 @@ public async Task Should_CreateRequestHandlerWithCorrectRequestOptions_When_Exec
session.Execute(new SimpleStatement("test query"), "testE");
}
- Mock.Get(requestHandlerFactoryMock).Verify(m => m.Create(session, serializer.GetCurrentSerializer(), It.IsAny(), config.RequestOptions["testE"]), Times.Once);
+ Mock.Get(requestHandlerFactoryMock).Verify(m => m.Create(session, clusterMock.InternalMetadata, serializer.GetCurrentSerializer(), It.IsAny(), config.RequestOptions["testE"]), Times.Once);
}
[Test]
@@ -85,7 +85,6 @@ public async Task Should_CreateRequestHandlerWithDefaultRequestOptions_When_Exec
var requestHandlerMock = Mock.Of();
var hostConnectionPoolFactoryMock = Mock.Of();
var clusterMock = Mock.Of();
- var serializer = SerializerManager.Default;
var config = new TestConfigurationBuilder
{
RequestHandlerFactory = requestHandlerFactoryMock,
@@ -104,10 +103,15 @@ public async Task Should_CreateRequestHandlerWithDefaultRequestOptions_When_Exec
Mock.Get(clusterMock).Setup(c => c.Configuration).Returns(config);
Mock.Get(requestHandlerMock).Setup(r => r.SendAsync()).Returns(Task.FromResult(new RowSet()));
- var session = new Session(clusterMock, config, null, serializer, null);
+ var session = new Session(clusterMock, config, null, null);
Mock.Get(requestHandlerFactoryMock)
- .Setup(m => m.Create(session, serializer.GetCurrentSerializer(), It.IsAny(), config.DefaultRequestOptions))
+ .Setup(m => m.Create(
+ session,
+ clusterMock.InternalMetadata,
+ config.SerializerManager.GetCurrentSerializer(),
+ It.IsAny(),
+ config.DefaultRequestOptions))
.Returns(requestHandlerMock);
if (async)
@@ -119,7 +123,14 @@ public async Task Should_CreateRequestHandlerWithDefaultRequestOptions_When_Exec
session.Execute(new SimpleStatement("test query"));
}
- Mock.Get(requestHandlerFactoryMock).Verify(m => m.Create(session, serializer.GetCurrentSerializer(), It.IsAny(), config.DefaultRequestOptions), Times.Once);
+ Mock.Get(requestHandlerFactoryMock).Verify(
+ m => m.Create(
+ session,
+ clusterMock.InternalMetadata,
+ config.SerializerManager.GetCurrentSerializer(),
+ It.IsAny(),
+ config.DefaultRequestOptions),
+ Times.Once);
}
}
}
\ No newline at end of file
diff --git a/src/Cassandra.Tests/Mapping/Linq/LinqEntryPointsTests.cs b/src/Cassandra.Tests/Mapping/Linq/LinqEntryPointsTests.cs
index 8601b3729..81ca31cf3 100644
--- a/src/Cassandra.Tests/Mapping/Linq/LinqEntryPointsTests.cs
+++ b/src/Cassandra.Tests/Mapping/Linq/LinqEntryPointsTests.cs
@@ -14,12 +14,12 @@
// limitations under the License.
//
-using Cassandra.Connections.Control;
using Cassandra.Data.Linq;
using Cassandra.Mapping;
-using Cassandra.Serialization;
using Cassandra.Tests.Mapping.Pocos;
+
using Moq;
+
using NUnit.Framework;
namespace Cassandra.Tests.Mapping.Linq
@@ -58,7 +58,7 @@ public void Deprecated_EntryPoint_Honors_Mapping_Defined()
public void Deprecated_EntryPoint_Uses_Table_Provided()
{
MappingConfiguration.Global.Define(new Map().TableName("tbl1"));
- var table = _session.GetTable( "linqTable");
+ var table = _session.GetTable("linqTable");
Assert.AreEqual(
@"SELECT BooleanValue, DateTimeValue, DecimalValue, DoubleValue, Int64Value, IntValue, StringValue, UuidValue FROM linqTable",
table.ToString());
@@ -68,7 +68,7 @@ public void Deprecated_EntryPoint_Uses_Table_Provided()
public void Deprecated_EntryPoint_Uses_Keyspace_Provided()
{
MappingConfiguration.Global.Define(new Map().TableName("tbl1"));
- var table = _session.GetTable( "linqTable", "linqKs");
+ var table = _session.GetTable("linqTable", "linqKs");
Assert.AreEqual(
@"SELECT BooleanValue, DateTimeValue, DecimalValue, DoubleValue, Int64Value, IntValue, StringValue, UuidValue FROM linqKs.linqTable",
table.ToString());
@@ -124,23 +124,14 @@ public void Table_Constructor_Uses_Provided_Mappings_With_Custom_Keyspace_And_Ta
table.Where(t => t.Int64Value == 1).ToString());
}
- private static Mock GetSessionMock(ISerializerManager serializer = null)
+ private static Mock GetSessionMock()
{
- if (serializer == null)
- {
- serializer = new SerializerManager(ProtocolVersion.MaxSupported);
- }
var sessionMock = new Mock(MockBehavior.Strict);
var config = new Configuration();
- var metadata = new Metadata(config);
- var ccMock = new Mock(MockBehavior.Strict);
- ccMock.Setup(cc => cc.Serializer).Returns(serializer);
- metadata.ControlConnection = ccMock.Object;
var clusterMock = new Mock();
- clusterMock.Setup(c => c.Metadata).Returns(metadata);
clusterMock.Setup(c => c.Configuration).Returns(config);
sessionMock.Setup(s => s.Cluster).Returns(clusterMock.Object);
return sessionMock;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Cassandra.Tests/Mapping/Linq/LinqMappingUnitTests.cs b/src/Cassandra.Tests/Mapping/Linq/LinqMappingUnitTests.cs
index c3a88d51f..57b5627ad 100644
--- a/src/Cassandra.Tests/Mapping/Linq/LinqMappingUnitTests.cs
+++ b/src/Cassandra.Tests/Mapping/Linq/LinqMappingUnitTests.cs
@@ -18,6 +18,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
using Cassandra.Data.Linq;
using Cassandra.Mapping;
using Cassandra.Metrics.Internal;
@@ -37,6 +38,13 @@ private ISession GetSession(RowSet result)
var clusterMock = new Mock();
clusterMock.Setup(c => c.Configuration).Returns(new Configuration());
+ var metadataMock = new Mock();
+ var metadataInternal = new Mock();
+ metadataInternal.SetupGet(m => m.ProtocolVersion).Returns(ProtocolVersion.V2);
+ metadataMock.Setup(m => m.GetClusterDescription()).Returns(new ClusterDescription(metadataInternal.Object));
+ metadataMock.Setup(m => m.GetClusterDescriptionAsync()).ReturnsAsync(new ClusterDescription(metadataInternal.Object));
+ clusterMock.SetupGet(c => c.Metadata).Returns(metadataMock.Object);
+
var sessionMock = new Mock(MockBehavior.Strict);
sessionMock.Setup(s => s.Keyspace).Returns(null);
sessionMock
@@ -48,7 +56,6 @@ private ISession GetSession(RowSet result)
.Returns(TestHelper.DelayedTask(result, 200))
.Verifiable();
sessionMock.Setup(s => s.PrepareAsync(It.IsAny())).Returns(TaskHelper.ToTask(GetPrepared("Mock query")));
- sessionMock.Setup(s => s.BinaryProtocolVersion).Returns(2);
sessionMock.Setup(s => s.Cluster).Returns(clusterMock.Object);
return sessionMock.Object;
}
@@ -156,7 +163,11 @@ public void Linq_CqlQuery_Automatically_Pages()
.Callback((s, profile) => stmt = (BoundStatement)s)
.Verifiable();
sessionMock.Setup(s => s.PrepareAsync(It.IsAny())).Returns(TaskHelper.ToTask(GetPrepared("Mock query")));
- sessionMock.Setup(s => s.BinaryProtocolVersion).Returns(2);
+ var metadataMock = new Mock();
+ var metadataInternal = new Mock();
+ metadataInternal.SetupGet(m => m.ProtocolVersion).Returns(ProtocolVersion.V2);
+ metadataMock.Setup(m => m.GetClusterDescription()).Returns(new ClusterDescription(metadataInternal.Object));
+ clusterMock.SetupGet(c => c.Metadata).Returns(metadataMock.Object);
rs.AutoPage = true;
rs.PagingState = new byte[] { 0, 0, 0 };
var counter = 0;
diff --git a/src/Cassandra.Tests/Mapping/Linq/LinqToCqlUnitTests.cs b/src/Cassandra.Tests/Mapping/Linq/LinqToCqlUnitTests.cs
index 188f68a42..f57b73b49 100644
--- a/src/Cassandra.Tests/Mapping/Linq/LinqToCqlUnitTests.cs
+++ b/src/Cassandra.Tests/Mapping/Linq/LinqToCqlUnitTests.cs
@@ -449,12 +449,7 @@ public void CreateTableCounterTest()
var actualCqlQueries = new List();
var sessionMock = new Mock(MockBehavior.Strict);
var config = new Configuration();
- var metadata = new Metadata(config);
- var ccMock = new Mock(MockBehavior.Strict);
- ccMock.Setup(cc => cc.Serializer).Returns(new SerializerManager(ProtocolVersion.MaxSupported));
- metadata.ControlConnection = ccMock.Object;
var clusterMock = new Mock();
- clusterMock.Setup(c => c.Metadata).Returns(metadata);
clusterMock.Setup(c => c.Configuration).Returns(config);
sessionMock.Setup(s => s.Cluster).Returns(clusterMock.Object);
sessionMock
@@ -462,6 +457,7 @@ public void CreateTableCounterTest()
.Returns(() => new RowSet())
.Callback(actualCqlQueries.Add)
.Verifiable();
+ sessionMock.Setup(s => s.ConnectAsync()).Returns(TaskHelper.Completed);
var session = sessionMock.Object;
var table1 = SessionExtensions.GetTable(session);
@@ -485,13 +481,9 @@ public void VirtualPropertiesTest()
var query2 = (from e in table where e.Id == 1 && e.Name == "MyName" select new { e.Id, e.Name, e.Description });
Assert.AreEqual("SELECT \"Id\", \"Name\", \"Description\" FROM \"InheritedEntity\" WHERE \"Id\" = ? AND \"Name\" = ?", query2.ToString());
var sessionMock = new Mock(MockBehavior.Strict);
+ sessionMock.Setup(s => s.ConnectAsync()).Returns(TaskHelper.Completed);
var config = new Configuration();
- var metadata = new Metadata(config);
- var ccMock = new Mock(MockBehavior.Strict);
- ccMock.Setup(cc => cc.Serializer).Returns(new SerializerManager(ProtocolVersion.MaxSupported));
- metadata.ControlConnection = ccMock.Object;
var clusterMock = new Mock();
- clusterMock.Setup(c => c.Metadata).Returns(metadata);
clusterMock.Setup(c => c.Configuration).Returns(config);
sessionMock.Setup(s => s.Cluster).Returns(clusterMock.Object);
string createQuery = null;
diff --git a/src/Cassandra.Tests/MetadataHelpers/TestHelpers/FakeSchemaParserFactory.cs b/src/Cassandra.Tests/MetadataHelpers/TestHelpers/FakeSchemaParserFactory.cs
index c448953da..c53d30418 100644
--- a/src/Cassandra.Tests/MetadataHelpers/TestHelpers/FakeSchemaParserFactory.cs
+++ b/src/Cassandra.Tests/MetadataHelpers/TestHelpers/FakeSchemaParserFactory.cs
@@ -1,12 +1,12 @@
-//
+//
// Copyright (C) DataStax Inc.
-//
+//
// 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.
@@ -17,6 +17,8 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
+
+using Cassandra.Connections.Control;
using Cassandra.MetadataHelpers;
namespace Cassandra.Tests.MetadataHelpers.TestHelpers
@@ -24,7 +26,7 @@ namespace Cassandra.Tests.MetadataHelpers.TestHelpers
internal class FakeSchemaParserFactory : ISchemaParserFactory
{
public ISchemaParser Create(
- Version cassandraVersion, Metadata parent, Func> udtResolver, ISchemaParser currentInstance = null)
+ Version cassandraVersion, IInternalMetadata parent, Func> udtResolver, ISchemaParser currentInstance = null)
{
var keyspaces = new ConcurrentDictionary();
diff --git a/src/Cassandra.Tests/ProtocolTests.cs b/src/Cassandra.Tests/ProtocolTests.cs
index 6540f154c..54dbbb3e1 100644
--- a/src/Cassandra.Tests/ProtocolTests.cs
+++ b/src/Cassandra.Tests/ProtocolTests.cs
@@ -22,9 +22,6 @@ namespace Cassandra.Tests
[TestFixture]
public class ProtocolTests
{
- private readonly Configuration _config = new TestConfigurationBuilder { AllowBetaProtocolVersions = false }.Build();
- private readonly Configuration _configBeta = new TestConfigurationBuilder { AllowBetaProtocolVersions = true }.Build();
-
[TestCase(ProtocolVersion.V4, ProtocolVersion.V5)]
[TestCase(ProtocolVersion.V2, ProtocolVersion.V3)]
[TestCase(ProtocolVersion.V3, ProtocolVersion.V4)]
@@ -36,7 +33,7 @@ public class ProtocolTests
public void GetLowerSupported_Should_NotSkipBetaVersions_When_AllowBetaProtocolVersionsTrue(
ProtocolVersion version, ProtocolVersion initialVersion)
{
- Assert.AreEqual(version, initialVersion.GetLowerSupported(_configBeta));
+ Assert.AreEqual(version, initialVersion.GetLowerSupported(true));
}
[TestCase(ProtocolVersion.V4, ProtocolVersion.V5)]
@@ -50,7 +47,7 @@ public void GetLowerSupported_Should_NotSkipBetaVersions_When_AllowBetaProtocolV
public void GetLowerSupported_Should_SkipBetaVersions_When_AllowBetaProtocolVersionsFalse(
ProtocolVersion version, ProtocolVersion initialVersion)
{
- Assert.AreEqual(version, initialVersion.GetLowerSupported(_config));
+ Assert.AreEqual(version, initialVersion.GetLowerSupported(false));
}
[TestCase(ProtocolVersion.V4, "4.0.0", "1.2.19")]
@@ -65,7 +62,7 @@ public void GetLowerSupported_Should_SkipBetaVersions_When_AllowBetaProtocolVers
public void GetHighestCommon_Should_Downgrade_To_Protocol_VX_With_Hosts(ProtocolVersion version,
params string[] cassandraVersions)
{
- Assert.AreEqual(version, ProtocolVersion.MaxSupported.GetHighestCommon(_config, cassandraVersions.Select(GetHost)));
+ Assert.AreEqual(version, ProtocolVersion.MaxSupported.GetHighestCommon(false, cassandraVersions.Select(GetHost)));
}
@@ -81,7 +78,7 @@ public void GetHighestCommon_Should_Downgrade_To_Protocol_VX_With_Hosts(Protocol
public void GetHighestCommon_Should_NotSkipBeta_When_AllowBetaVersionIsTrue(ProtocolVersion version,
params string[] cassandraVersions)
{
- Assert.AreEqual(version, ProtocolVersion.MaxSupported.GetHighestCommon(_configBeta, cassandraVersions.Select(GetHost)));
+ Assert.AreEqual(version, ProtocolVersion.MaxSupported.GetHighestCommon(true, cassandraVersions.Select(GetHost)));
}
[TestCase(ProtocolVersion.V3, "6.0/3.10.2", "4.8.1/2.1.17", "5.1/3.0.13")]
@@ -89,7 +86,7 @@ public void GetHighestCommon_Should_NotSkipBeta_When_AllowBetaVersionIsTrue(Prot
public void GetHighestCommon_Should_Downgrade_To_Protocol_VX_With_Dse_Hosts(ProtocolVersion version,
params string[] cassandraVersions)
{
- Assert.AreEqual(version, ProtocolVersion.MaxSupported.GetHighestCommon(_config, cassandraVersions.Select(GetHost)));
+ Assert.AreEqual(version, ProtocolVersion.MaxSupported.GetHighestCommon(false, cassandraVersions.Select(GetHost)));
}
[TestCase(ProtocolVersion.V4, "4.0.0")]
@@ -102,7 +99,7 @@ public void GetHighestCommon_Should_Downgrade_To_Protocol_VX_With_Dse_Hosts(Prot
public void GetHighestCommon_Should_Not_Downgrade_Protocol_With_Hosts(ProtocolVersion version,
params string[] cassandraVersions)
{
- Assert.AreEqual(version, version.GetHighestCommon(_config, cassandraVersions.Select(GetHost)));
+ Assert.AreEqual(version, version.GetHighestCommon(false, cassandraVersions.Select(GetHost)));
}
[TestCase(ProtocolVersion.V5, "4.0.0")]
@@ -115,7 +112,7 @@ public void GetHighestCommon_Should_Not_Downgrade_Protocol_With_Hosts(ProtocolVe
public void GetHighestCommon_Should_Not_Downgrade_Protocol_With_Hosts_When_AllowBetaVersionIsTrue(ProtocolVersion version,
params string[] cassandraVersions)
{
- Assert.AreEqual(version, version.GetHighestCommon(_configBeta, cassandraVersions.Select(GetHost)));
+ Assert.AreEqual(version, version.GetHighestCommon(true, cassandraVersions.Select(GetHost)));
}
[TestCase(ProtocolVersion.V4, "5.1.7/3.0.13", "5.0.13/3.0.11", "2.2.9")]
@@ -125,7 +122,7 @@ public void GetHighestCommon_Should_Not_Downgrade_Protocol_With_Hosts_When_Allow
public void GetHighestCommon_Should_Not_Downgrade_Protocol_With_Dse_Hosts(ProtocolVersion version,
params string[] cassandraVersions)
{
- Assert.AreEqual(version, version.GetHighestCommon(_config, cassandraVersions.Select(GetHost)));
+ Assert.AreEqual(version, version.GetHighestCommon(false, cassandraVersions.Select(GetHost)));
}
private static Host GetHost(string cassandraVersion, int index)
diff --git a/src/Cassandra.Tests/RequestExecutionTests.cs b/src/Cassandra.Tests/RequestExecutionTests.cs
index 20be99187..bdc629fa3 100644
--- a/src/Cassandra.Tests/RequestExecutionTests.cs
+++ b/src/Cassandra.Tests/RequestExecutionTests.cs
@@ -18,6 +18,7 @@
using System.Collections.Generic;
using System.Net;
using Cassandra.Connections;
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Observers;
using Cassandra.Requests;
@@ -40,6 +41,7 @@ public void Should_ThrowException_When_NoValidHosts(bool currentHostRetry)
Mock.Get(requestHandlerFactory)
.Setup(r => r.Create(
It.IsAny(),
+ It.IsAny(),
It.IsAny(),
It.IsAny(),
It.IsAny(),
@@ -68,6 +70,7 @@ public void Should_NotThrowException_When_AValidHostIsObtained(bool currentHostR
Mock.Get(requestHandlerFactory)
.Setup(r => r.Create(
It.IsAny(),
+ It.IsAny(),
It.IsAny(),
It.IsAny(),
It.IsAny(),
@@ -103,6 +106,7 @@ public void Should_SendRequest_When_AConnectionIsObtained(bool currentHostRetry)
Mock.Get(requestHandlerFactory)
.Setup(r => r.Create(
It.IsAny(),
+ It.IsAny(),
It.IsAny(),
It.IsAny(),
It.IsAny(),
@@ -152,6 +156,7 @@ public void Should_RetryRequestToSameHost_When_ConnectionFailsAndRetryDecisionIs
Mock.Get(requestHandlerFactory)
.Setup(r => r.Create(
It.IsAny(),
+ It.IsAny(),
It.IsAny(),
It.IsAny(),
It.IsAny(),
diff --git a/src/Cassandra.Tests/RequestHandlerTests.cs b/src/Cassandra.Tests/RequestHandlerTests.cs
index 658d41814..8cd0b84d8 100644
--- a/src/Cassandra.Tests/RequestHandlerTests.cs
+++ b/src/Cassandra.Tests/RequestHandlerTests.cs
@@ -308,7 +308,7 @@ public void GetRequest_With_Timestamp_Generator_Empty_Value()
Cassandra.Policies.DefaultLoadBalancingPolicy, Cassandra.Policies.DefaultReconnectionPolicy,
Cassandra.Policies.DefaultRetryPolicy, Cassandra.Policies.DefaultSpeculativeExecutionPolicy,
new NoTimestampGenerator());
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Get());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Create());
var request = RequestHandler.GetRequest(
statement, RequestHandlerTests.Serializer, config.DefaultRequestOptions);
@@ -341,7 +341,7 @@ public void GetRequest_With_Timestamp_Generator_Empty_Value_With_Statement_Times
Cassandra.Policies.DefaultLoadBalancingPolicy, Cassandra.Policies.DefaultReconnectionPolicy,
Cassandra.Policies.DefaultRetryPolicy, Cassandra.Policies.DefaultSpeculativeExecutionPolicy,
new NoTimestampGenerator());
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Get());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Create());
var request = RequestHandler.GetRequest(statement, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -372,7 +372,7 @@ public void GetRequest_Batch_With_64K_Queries()
batch.Add(new SimpleStatement("QUERY"));
}
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Get());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Create());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -390,7 +390,7 @@ public void GetRequest_Batch_With_Timestamp_Generator()
// To microsecond precision
startDate = startDate.Subtract(TimeSpan.FromTicks(startDate.Ticks % 10));
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Get());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Create());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -426,7 +426,7 @@ public void GetRequest_Batch_With_Empty_Timestamp_Generator()
Cassandra.Policies.DefaultRetryPolicy, Cassandra.Policies.DefaultSpeculativeExecutionPolicy,
new NoTimestampGenerator());
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Get());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), policies, PoolingOptions.Create());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
@@ -453,7 +453,7 @@ public void GetRequest_Batch_With_Provided_Timestamp()
providedTimestamp = providedTimestamp.Subtract(TimeSpan.FromTicks(providedTimestamp.Ticks % 10));
batch.SetTimestamp(providedTimestamp);
- var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Get());
+ var config = RequestHandlerTests.GetConfig(new QueryOptions(), Cassandra.Policies.DefaultPolicies, PoolingOptions.Create());
var request = RequestHandler.GetRequest(batch, Serializer, config.DefaultRequestOptions);
var bodyBuffer = GetBodyBuffer(request);
diff --git a/src/Cassandra.Tests/Requests/FakeRequestHandlerFactory.cs b/src/Cassandra.Tests/Requests/FakeRequestHandlerFactory.cs
index 26ec65020..306860647 100644
--- a/src/Cassandra.Tests/Requests/FakeRequestHandlerFactory.cs
+++ b/src/Cassandra.Tests/Requests/FakeRequestHandlerFactory.cs
@@ -16,10 +16,12 @@
using System;
using System.Threading.Tasks;
+using Cassandra.Connections.Control;
using Cassandra.ExecutionProfiles;
using Cassandra.Requests;
using Cassandra.Serialization;
using Cassandra.SessionManagement;
+
using Moq;
namespace Cassandra.Tests.Requests
@@ -28,24 +30,35 @@ internal class FakeRequestHandlerFactory : IRequestHandlerFactory
{
private readonly Action _executeCallback;
private readonly Func _rs;
-
+
public FakeRequestHandlerFactory(Action