From 12c9a56528dd95ae08ef3568a8555105e54826df Mon Sep 17 00:00:00 2001 From: Ralitsa Todorova Date: Thu, 11 Oct 2018 18:24:00 +0300 Subject: [PATCH 1/4] NY-4311: Implement cache for prepared statements - added session cache for prepared statements; - added codec for AuthenticationProvider type; Signed-off-by: Ralitsa Todorova --- .../codecs/AuthenticationProviderCodec.java | 67 +++++++++++++++++++ .../components/PreparedStatementsCache.java | 65 ++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java create mode 100644 src/main/java/biz/nynja/account/components/PreparedStatementsCache.java diff --git a/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java new file mode 100644 index 0000000..24b096f --- /dev/null +++ b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.codecs; + +import java.nio.ByteBuffer; + +import com.datastax.driver.core.ProtocolVersion; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; +import com.datastax.driver.core.exceptions.InvalidTypeException; + +import biz.nynja.account.models.AuthenticationProvider; + + +public class AuthenticationProviderCodec extends TypeCodec { + + private final TypeCodec innerCodec; + + private final UserType userType; + + public AuthenticationProviderCodec(TypeCodec innerCodec, Class javaType) { + super(innerCodec.getCqlType(), javaType); + this.innerCodec = innerCodec; + this.userType = (UserType) innerCodec.getCqlType(); + } + + @Override + public ByteBuffer serialize(AuthenticationProvider value, ProtocolVersion protocolVersion) + throws InvalidTypeException { + return innerCodec.serialize(toUDTValue(value), protocolVersion); + } + + @Override + public AuthenticationProvider deserialize(ByteBuffer bytes, ProtocolVersion protocolVersion) + throws InvalidTypeException { + return toAuthenticationProvider(innerCodec.deserialize(bytes, protocolVersion)); + } + + @Override + public AuthenticationProvider parse(String value) throws InvalidTypeException { + return value == null || value.isEmpty() || value.equals("NULL") ? null + : toAuthenticationProvider(innerCodec.parse(value)); + } + + @Override + public String format(AuthenticationProvider value) throws InvalidTypeException { + return value == null ? null : innerCodec.format(toUDTValue(value)); + } + + protected AuthenticationProvider toAuthenticationProvider(UDTValue value) { + if (value == null) { + return null; + } else { + AuthenticationProvider authProvider = new AuthenticationProvider(); + authProvider.setType(value.getString("type")); + authProvider.setValue(value.getString("value")); + return authProvider; + } + } + + protected UDTValue toUDTValue(AuthenticationProvider value) { + return value == null ? null + : userType.newValue().setString("type", value.getType()).setString("value", value.getValue()); + } +} \ No newline at end of file diff --git a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java new file mode 100644 index 0000000..7d1abaf --- /dev/null +++ b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; + +import biz.nynja.account.codecs.AuthenticationProviderCodec; +import biz.nynja.account.models.AuthenticationProvider; + +@Configuration +public class PreparedStatementsCache { + + @Value("${spring.data.cassandra.keyspace-name}") + private String keyspace; + + @Autowired + private Session session; + + private static Map statementsCache; + + @PostConstruct + public void init() { + statementsCache = new ConcurrentHashMap<>(); + registerAuthenticationProviderCodec(); + } + + private Map getPreparedStatementsCache() { + return statementsCache; + } + + public PreparedStatement getPreparedStatement(String cql) { + if (getPreparedStatementsCache().containsKey(cql)) { + return getPreparedStatementsCache().get(cql); + } else { + PreparedStatement statement = session.prepare(cql); + getPreparedStatementsCache().put(cql, statement); + return statement; + } + } + + private void registerAuthenticationProviderCodec() { + UserType authenticationProviderType = session.getCluster().getMetadata().getKeyspace(keyspace) + .getUserType("authenticationprovider"); + TypeCodec authenticationProviderTypeCodec = session.getCluster().getConfiguration().getCodecRegistry() + .codecFor(authenticationProviderType); + + AuthenticationProviderCodec addressCodec = new AuthenticationProviderCodec(authenticationProviderTypeCodec, + AuthenticationProvider.class); + session.getCluster().getConfiguration().getCodecRegistry().register(addressCodec); + } +} -- GitLab From b9808cf3c411e5e9c795f03e21d3061862a25f64 Mon Sep 17 00:00:00 2001 From: Ralitsa Todorova Date: Thu, 11 Oct 2018 18:25:42 +0300 Subject: [PATCH 2/4] NY-4311: Use PreparedStatements instead of SimpleStatements Cassandra operations Signed-off-by: Ralitsa Todorova --- .../account/components/StatementsPool.java | 65 +++++++++++++++++++ .../AccountRepositoryAdditionalImpl.java | 55 +++++++--------- 2 files changed, 89 insertions(+), 31 deletions(-) create mode 100644 src/main/java/biz/nynja/account/components/StatementsPool.java diff --git a/src/main/java/biz/nynja/account/components/StatementsPool.java b/src/main/java/biz/nynja/account/components/StatementsPool.java new file mode 100644 index 0000000..18b1e1b --- /dev/null +++ b/src/main/java/biz/nynja/account/components/StatementsPool.java @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2018 Nynja Inc. All rights reserved. + */ +package biz.nynja.account.components; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.datastax.driver.core.BoundStatement; + +import biz.nynja.account.models.AuthenticationProvider; + +@Service +public class StatementsPool { + + @Autowired + PreparedStatementsCache preparedStatementsCache; + + public BoundStatement addAuthenticationProviderToProfile(UUID profileId, AuthenticationProvider authProvider) { + String cql = "UPDATE profile SET authenticationproviders = authenticationproviders + ? WHERE profileid = ? ;"; + Set toBeAdded = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(authProvider))); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(toBeAdded, profileId); + return bound; + } + + public BoundStatement insertProfileByAuthenticationProvider(UUID profileId, String authPovider, + String authProviderType) { + String cql = "INSERT INTO profilebyauthenticationprovider (authenticationprovider, authenticationprovidertype, profileid) VALUES (?, ?, ?) ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(authPovider, authProviderType, + profileId); + return bound; + } + + public BoundStatement deleteAuthenicationProviderFromProfile(UUID profileId, AuthenticationProvider authProvider) { + String cql = "UPDATE profile SET authenticationproviders = authenticationproviders - ? WHERE profileid = ? ;"; + Set toBeDeleted = Collections + .unmodifiableSet(new HashSet<>(Arrays.asList(authProvider))); + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(toBeDeleted, profileId); + return bound; + } + + public BoundStatement deleteProfileByAuthenticationProvider(String authProvider, String authProviderType) { + String cql = "DELETE FROM profilebyauthenticationprovider where authenticationprovider = ? and authenticationprovidertype = ? ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(authProvider, authProviderType); + return bound; + } + + public BoundStatement deleteBackupAuthenticationProviderFromProfile(UUID profileId) { + String cql = "DELETE backupauthenticationprovider FROM profile WHERE profileid = ? ; "; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(profileId); + return bound; + } + + public BoundStatement deleteCreationProviderFromAccount(UUID accountId) { + String cql = "DELETE authenticationprovidertype, authenticationprovider FROM account WHERE accountid = ? ;"; + BoundStatement bound = preparedStatementsCache.getPreparedStatement(cql).bind(accountId); + return bound; + } +} diff --git a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java index 76a684e..e760dab 100644 --- a/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java +++ b/src/main/java/biz/nynja/account/repositories/AccountRepositoryAdditionalImpl.java @@ -19,12 +19,13 @@ import org.springframework.data.cassandra.core.WriteResult; import org.springframework.stereotype.Service; import com.datastax.driver.core.BatchStatement; +import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Session; -import com.datastax.driver.core.SimpleStatement; -import com.datastax.driver.core.Statement; import biz.nynja.account.components.AccountServiceHelper; +import biz.nynja.account.components.StatementsPool; + import biz.nynja.account.grpc.AuthProviderDetails; import biz.nynja.account.grpc.CompletePendingAccountCreationRequest; import biz.nynja.account.grpc.UpdateAccountRequest; @@ -51,6 +52,9 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio @Autowired private CassandraTemplate cassandraTemplate; + @Autowired + private StatementsPool statementsPool; + @Autowired private AccountRepository accountRepository; @@ -482,21 +486,18 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio @Override public boolean addAuthenticationProvider(UUID profileId, AuthenticationProvider authProvider) { - BatchStatement batchOperations = new BatchStatement(); + BatchStatement batch = new BatchStatement(); ResultSet wr = null; try { - Statement updateAuthProvidersInProfile = new SimpleStatement( - "UPDATE profile SET authenticationproviders = authenticationproviders + {('" - + authProvider.getType() + "', '" + authProvider.getValue() + "')} WHERE profileid = " - + profileId.toString()); - Statement insertInProfileByAuthProvider = new SimpleStatement(" INSERT INTO profilebyauthenticationprovider " - + "(authenticationprovider, authenticationprovidertype, profileid) VALUES ('" - + authProvider.getValue() + "', '" + authProvider.getType() + "', " + profileId + ");"); - - batchOperations.add(updateAuthProvidersInProfile); - batchOperations.add(insertInProfileByAuthProvider); - wr = session.execute(batchOperations); + BoundStatement updateAuthProvidersInProfile = statementsPool.addAuthenticationProviderToProfile(profileId, + authProvider); + batch.add(updateAuthProvidersInProfile); + BoundStatement insertInProfileByAuthProvider = statementsPool + .insertProfileByAuthenticationProvider(profileId, authProvider.getValue(), authProvider.getType()); + batch.add(insertInProfileByAuthProvider); + + wr = session.execute(batch); } catch (IllegalArgumentException | IllegalStateException e) { logger.info("Exception while adding new authenication provider to profile with id: {}.", profileId); logger.debug("Exception while adding new authenication provider to profile with id: {}, {}", profileId, @@ -514,43 +515,35 @@ public class AccountRepositoryAdditionalImpl implements AccountRepositoryAdditio } @Override - public boolean deleteAuthenticationProvider(Profile profile, - AuthenticationProvider authProvider) { + public boolean deleteAuthenticationProvider(Profile profile, AuthenticationProvider authProvider) { BatchStatement batch = new BatchStatement(); ResultSet rs = null; // Remove authentication provider form list of authentication providers in profile - Statement st1 = new SimpleStatement("UPDATE profile SET authenticationproviders = authenticationproviders - {('" - + authProvider.getType() + "', '" + authProvider.getValue() - + "')} WHERE profileid = " + profile.getProfileId().toString()); + BoundStatement st1 = statementsPool.deleteAuthenicationProviderFromProfile(profile.getProfileId(), + authProvider); batch.add(st1); // Remove record for profile by this authentication provider - Statement st2 = new SimpleStatement( - "DELETE FROM profilebyauthenticationprovider where authenticationprovider = '" - + authProvider.getValue() + "' and authenticationprovidertype = '" - + authProvider.getType() + "';"); + BoundStatement st2 = statementsPool.deleteProfileByAuthenticationProvider(authProvider.getValue(), + authProvider.getType()); batch.add(st2); // The requested authentication provider is set as a backup provider. We have to delete the reference. if (profile.getBackupAuthenticationProvider() != null && profile.getBackupAuthenticationProvider().equals(authProvider)) { - Statement st3 = new SimpleStatement("DELETE backupauthenticationprovider FROM profile WHERE profileid = " - + profile.getProfileId().toString()); + BoundStatement st3 = statementsPool.deleteBackupAuthenticationProviderFromProfile(profile.getProfileId()); batch.add(st3); } // Check if there is an account, created using this authentication provider - Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper( - authProvider.getValue(), authProvider.getType()); - + Account account = accountServiceHelper.getAccountByAuthenticationProviderHelper(authProvider.getValue(), + authProvider.getType()); // Delete authentication provider from account if (account != null) { - Statement st4 = new SimpleStatement( - "DELETE authenticationprovidertype, authenticationprovider FROM account WHERE accountid = " - + account.getAccountId().toString()); + BoundStatement st4 = statementsPool.deleteCreationProviderFromAccount(account.getAccountId()); batch.add(st4); } -- GitLab From 510ca2711b2b5f90ae033124696e8ebc8a4d6f3b Mon Sep 17 00:00:00 2001 From: Ralitsa Todorova Date: Mon, 15 Oct 2018 10:22:59 +0300 Subject: [PATCH 3/4] NY-4311: Added new line at the end of file Signed-off-by: Ralitsa Todorova --- .../biz/nynja/account/codecs/AuthenticationProviderCodec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java index 24b096f..c1260f7 100644 --- a/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java +++ b/src/main/java/biz/nynja/account/codecs/AuthenticationProviderCodec.java @@ -64,4 +64,4 @@ public class AuthenticationProviderCodec extends TypeCodec Date: Thu, 18 Oct 2018 16:45:02 +0300 Subject: [PATCH 4/4] NY-4311: Use Constructor-based DI Signed-off-by: Ralitsa Todorova --- .../components/PreparedStatementsCache.java | 18 +++++++++++------- .../account/components/StatementsPool.java | 6 +++++- .../account/configuration/CassandraConfig.java | 4 ++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java index 7d1abaf..91b9b32 100644 --- a/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java +++ b/src/main/java/biz/nynja/account/components/PreparedStatementsCache.java @@ -9,7 +9,6 @@ import java.util.concurrent.ConcurrentHashMap; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import com.datastax.driver.core.PreparedStatement; @@ -19,19 +18,24 @@ import com.datastax.driver.core.UDTValue; import com.datastax.driver.core.UserType; import biz.nynja.account.codecs.AuthenticationProviderCodec; +import biz.nynja.account.configuration.CassandraConfig; import biz.nynja.account.models.AuthenticationProvider; @Configuration public class PreparedStatementsCache { - @Value("${spring.data.cassandra.keyspace-name}") - private String keyspace; + private final CassandraConfig cassandraConfig; - @Autowired - private Session session; + private final Session session; private static Map statementsCache; + @Autowired + public PreparedStatementsCache(Session session, CassandraConfig cassandraConfig) { + this.session = session; + this.cassandraConfig = cassandraConfig; + } + @PostConstruct public void init() { statementsCache = new ConcurrentHashMap<>(); @@ -53,8 +57,8 @@ public class PreparedStatementsCache { } private void registerAuthenticationProviderCodec() { - UserType authenticationProviderType = session.getCluster().getMetadata().getKeyspace(keyspace) - .getUserType("authenticationprovider"); + UserType authenticationProviderType = session.getCluster().getMetadata() + .getKeyspace(cassandraConfig.getConfiguredKeyspaceName()).getUserType("authenticationprovider"); TypeCodec authenticationProviderTypeCodec = session.getCluster().getConfiguration().getCodecRegistry() .codecFor(authenticationProviderType); diff --git a/src/main/java/biz/nynja/account/components/StatementsPool.java b/src/main/java/biz/nynja/account/components/StatementsPool.java index 18b1e1b..5ee4b81 100644 --- a/src/main/java/biz/nynja/account/components/StatementsPool.java +++ b/src/main/java/biz/nynja/account/components/StatementsPool.java @@ -19,8 +19,12 @@ import biz.nynja.account.models.AuthenticationProvider; @Service public class StatementsPool { + private final PreparedStatementsCache preparedStatementsCache; + @Autowired - PreparedStatementsCache preparedStatementsCache; + public StatementsPool(PreparedStatementsCache preparedStatementsCache) { + this.preparedStatementsCache = preparedStatementsCache; + } public BoundStatement addAuthenticationProviderToProfile(UUID profileId, AuthenticationProvider authProvider) { String cql = "UPDATE profile SET authenticationproviders = authenticationproviders + ? WHERE profileid = ? ;"; diff --git a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java index 4706603..739d506 100644 --- a/src/main/java/biz/nynja/account/configuration/CassandraConfig.java +++ b/src/main/java/biz/nynja/account/configuration/CassandraConfig.java @@ -70,4 +70,8 @@ public class CassandraConfig extends AbstractCassandraConfiguration { protected List getStartupScripts() { return super.getStartupScripts(); } + + public String getConfiguredKeyspaceName() { + return getKeyspaceName(); + } } -- GitLab