I am trying to delete every 100 records read from a file in 3 tables,using spring jdbc batch delete .If i wrap the logic inside a transactionTemplate, is it going to work as expected, e.g lets say i am creating 3 batch out of 300 records and wrapping the logic inside a transaction ,then is the transaction going to roll back 1st and 2nd batch, if 3rd batch got a problem.My code is included in the question for reference. I am writing the below code to achieve what i have explained above, is my code correct?
TransactionTemplate txnTemplate = new TransactionTemplate(txnManager);
txnTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
public void doInTransactionWithoutResult(final TransactionStatus status) {
try {
deleteFromABCTable(jdbcTemplate, successList);
deleteFromDEFTable(jdbcTemplate, successList);
deleteFromXYZTable(jdbcTemplate, successList);
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
}
});
My delete methods :-
private void deleteFromABCTable(JdbcTemplate jdbcTemplate, List
successList) {
try {
jdbcTemplate.batchUpdate(
"delete from ABC where document_id in (select document_id
from ABC where item in(?)))",
new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i)
throws SQLException {
ps.setString(0, successList.get(i));
} });
} catch (Exception e) { }
Related
I have a strange situation with my java play framework (2.3) application. All works fine If I have deployed my applications close (geographically) my database mysql. The request, with connections to database works fine and fast. But, last day, I moved the database remotely, in another country. The application go on fine, but, each time I create a JPA Entity Manager (and I think the application create a new connections to remote database) the connections is very very slowly. And the result is that all request are extremely slow.
According your experience there is a way to optimize this situation via application?
Below my controller java code:
#Transactional
public Result testperson() {
Person person= JPAEntityManager.find(Person .class, "XXXXXX");
person.setAddress("XXXXXXX");
JPA.em().persist(person);
return ok("");
}
The #Transactional annotation intercept a play framework jpa implementation for the connections:
public static <T> F.Promise<T> withTransactionAsync(String name, boolean readOnly, play.libs.F.Function0<F.Promise<T>> block) throws Throwable {
EntityManager em = null;
EntityTransaction tx = null;
try {
em = JPA.em(name);
JPA.bindForCurrentThread(em);
if(!readOnly) {
tx = em.getTransaction();
tx.begin();
}
F.Promise<T> result = block.apply();
final EntityManager fem = em;
final EntityTransaction ftx = tx;
F.Promise<T> committedResult = result.map(new F.Function<T, T>() {
#Override
public T apply(T t) throws Throwable {
try {
if(ftx != null) {
if(ftx.getRollbackOnly()) {
ftx.rollback();
} else {
ftx.commit();
}
}
} finally {
fem.close();
}
return t;
}
});
committedResult.onFailure(new F.Callback<Throwable>() {
#Override
public void invoke(Throwable t) {
if (ftx != null) {
try { if (ftx.isActive()) ftx.rollback(); } catch(Throwable e) {}
}
fem.close();
}
});
return committedResult;
} catch(Throwable t) {
if(tx != null) {
try { tx.rollback(); } catch(Throwable e) {}
}
if(em != null) {
em.close();
}
throw t;
} finally {
JPA.bindForCurrentThread(null);
}
}
The JPA.em() create a new EntityManager...
All connections details are default for the play framework: https://www.playframework.com/documentation/2.3.x/SettingsJDBC
Maybe Is there a problem with MySQl database during remote connections?
Can there be some settings to set on the database side to improve a remote connection?
Thanks in advance!
How long does SELECT 1 take? That gives you a good clue of the new overhead for every SQL statement because of reaching into "another country".
If it turns out that there are "too many" queries, consider wrapping a set of them in a Stored Procedure. Then have your app CALL the SP -- this will be one roundtrip, not many.
I need to migrate to Kinesis library to version 2.2.11 so I followed the tutorial: https://docs.aws.amazon.com/streams/latest/dev/kcl-migration.html
I need to run multiple instances of my consumer app, so every one of them needs to have an unique application name in order to have a separate lease table in DynamoDb.
When initializing the consumer Kinesis runs DynamoDBLeaseRefresher.createLeaseTableIfNotExists which checks if a new table needs to be created for this application name and creates one if it cannot be found.
So 2 operations are performed:
DescribeTable - it returns the table info or throws a ResourceNotFoundExecption,
if needed - CreateTable.
The problem for me is with the DescribeTable method. When I am looking for an existing table it returns it with no problem. But when I am looking for a non-existent table it throws the ResourceNotFoundExecption -> so far so good. Unfortunately it then gets wrapped and is now:
java.util.concurrent.CompletionException: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: software.amazon.awssdk.awscore.exception.AwsServiceException$Builder.extendedRequestId(Ljava/lang/String;)Lsoftware/amazon/awssdk/awscore/exception/AwsServiceException$Builder;
and the app expecting ResourceNotFoundException gets something different instead and crashes.
The wrapped exception message is a bit misleading: "Unable to execute HTTP request" since the request was performed and returned the proper message: "Resource not found".
Funny thing is that it sometimes works, the exception does not get wrapped, the CreateTable operation is performed and the consumer starts properly.
I have made a workaround for it for now where I just create the table before the initialization of the LeaseCoordinator, so it always gets the existing table.
here is my code:
public KinesisStreamReaderService(String streamName, String applicationName, String regionName) {
KinesisAsyncClient kinesisClient = KinesisAsyncClient.builder()
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.region(Region.of(connectionProperties.getRegion()))
.httpClientBuilder(createHttpClientBuilder())
.build();
DynamoDbAsyncClient dynamoClient = DynamoDbAsyncClient.builder().region(Region.of(regionName)).build();
CloudWatchAsyncClient cloudWatchClient = CloudWatchAsyncClient.builder().region(Region.of(regionName)).build();
// if(!dynamoDbTableExists(dynamoClient, applicationName)) {
// createDynamoDbTable(dynamoClient, applicationName);
// }
ConfigsBuilder configsBuilder = new ConfigsBuilder(streamName, applicationName, kinesisClient,
dynamoClient, cloudWatchClient, workerId(), KinesisReaderProcessor::new);
configsBuilder.retrievalConfig().initialPositionInStreamExtended(
InitialPositionInStreamExtended.newInitialPosition(
InitialPositionInStream.LATEST));
scheduler = new Scheduler(
configsBuilder.checkpointConfig(),
configsBuilder.coordinatorConfig(),
configsBuilder.leaseManagementConfig(),
configsBuilder.lifecycleConfig(),
configsBuilder.metricsConfig(),
configsBuilder.processorConfig(),
configsBuilder.retrievalConfig().retrievalSpecificConfig(new PollingConfig(streamName, kinesisClient))
);
}
private void createDynamoDbTable(DynamoDbAsyncClient dynamoClient, String applicationName) {
log.info("Creating new lease table: {}", applicationName);
CompletableFuture<CreateTableResponse> createTableFuture = dynamoClient
.createTable(CreateTableRequest.builder()
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
.tableName(applicationName)
.keySchema(KeySchemaElement.builder().attributeName("leaseKey").keyType(KeyType.HASH).build())
.attributeDefinitions(AttributeDefinition.builder().attributeName("leaseKey").attributeType(
ScalarAttributeType.S).build())
.build());
try {
CreateTableResponse createTableResponse = createTableFuture.get();
log.debug("Created new lease table: {}", createTableResponse.tableDescription().tableName());
} catch (InterruptedException | ExecutionException e) {
throw new DataStreamException(e.getMessage(), e);
}
}
private boolean dynamoDbTableExists(DynamoDbAsyncClient dynamoClient, String tableName) {
CompletableFuture<DescribeTableResponse> describeTableResponseCompletableFutureNew = dynamoClient
.describeTable(DescribeTableRequest.builder()
.tableName(tableName).build());
try {
DescribeTableResponse describeTableResponseNew = describeTableResponseCompletableFutureNew
.get();
return nonNull(describeTableResponseNew);
} catch (InterruptedException | ExecutionException e) {
log.info(e.getMessage(), e);
}
return false;
}
private static String workerId() {
String workerId;
try {
workerId = format("%s_%s", getLocalHost().getCanonicalHostName(), randomUUID().toString());
} catch (UnknownHostException e) {
workerId = randomUUID().toString();
}
return workerId;
}
#Override
public void read(Consumer<String> consumer) {
this.consumer = consumer;
scheduler.run();
}
private class KinesisReaderProcessor implements ShardRecordProcessor {
private String shardId;
#Override
public void initialize(InitializationInput initializationInput) {
this.shardId = initializationInput.shardId();
log.info("Initializing record processor for shard: {}", shardId);
}
#Override
public void processRecords(ProcessRecordsInput processRecordsInput) {
log.debug("Checking shard {} for new records", shardId);
List<KinesisClientRecord> records = processRecordsInput.records();
if (!records.isEmpty()) {
log.debug("Processing {} records from kinesis stream shard {}", records.size(), shardId);
records.forEach(record -> {
String json = UTF_8.decode(record.data()).toString();
log.info(json);
consumer.accept(json);
});
}
}
#Override
public void leaseLost(LeaseLostInput leaseLostInput) {
log.info("Record processor has lost lease, terminating");
}
#Override
public void shardEnded(ShardEndedInput shardEndedInput) {
try {
shardEndedInput.checkpointer().checkpoint();
} catch (ShutdownException | InvalidStateException e) {
log.error(e.getMessage(), e);
}
}
#Override
public void shutdownRequested(ShutdownRequestedInput shutdownRequestedInput) {
try {
shutdownRequestedInput.checkpointer().checkpoint();
} catch (ShutdownException | InvalidStateException e) {
log.error(e.getMessage(), e);
}
}
}
}
Am I missing some configuration for the scheduler or something? Why is it sometimes working?
Thanks
Edit:
The problem is this block of code in DynamoDBLeaseRefresher.tableStatus() is invoked to check if the table exists:
DescribeTableResponse result;
try {
try {
result =
(DescribeTableResponse)FutureUtils.resolveOrCancelFuture(this.dynamoDBClient.describeTable(request), this.dynamoDbRequestTimeout);
} catch (ExecutionException var5) {
throw exceptionManager.apply(var5.getCause());
} catch (InterruptedException var6) {
throw new DependencyException(var6);
}
} catch (ResourceNotFoundException var7) {
log.debug("Got ResourceNotFoundException for table {} in leaseTableExists, returning false.", this.table);
return null;
}
and in my case it should get ResourceNotFoundException if the table is not found, but as I said the expection gets wrapped to CompletionException before it reaches the appropriate catch block and is caught in the code here:
catch (ExecutionException var5) {
throw exceptionManager.apply(var5.getCause());
This is happening 20 times in the loop while trying to Initialize the LeaseCoordinator and then just stops trying to initialize the connection. (As mentioned above it works occasionally, but that makes it even stranger to me)
With my workaround it only needs 1 try to get initialized
You don't need to create a lease table manually - DynamoDBLeaseCoordinator will create one if not exists on initialization and wait until it exists:
#Override
public void initialize() throws ProvisionedThroughputException, DependencyException, IllegalStateException {
final boolean newTableCreated =
leaseRefresher.createLeaseTableIfNotExists(initialLeaseTableReadCapacity, initialLeaseTableWriteCapacity);
if (newTableCreated) {
log.info("Created new lease table for coordinator with initial read capacity of {} and write capacity of {}.",
initialLeaseTableReadCapacity, initialLeaseTableWriteCapacity);
}
// Need to wait for table in active state.
final long secondsBetweenPolls = 10L;
final long timeoutSeconds = 600L;
final boolean isTableActive = leaseRefresher.waitUntilLeaseTableExists(secondsBetweenPolls, timeoutSeconds);
if (!isTableActive) {
throw new DependencyException(new IllegalStateException("Creating table timeout"));
}
}
The issue in your case, I think, is that it's eventually created and you probably should periodically check until table appears - like DynamoDBLeaseCoordinator#initialize() does.
I am getting pesimistlockexception when trying to persist multiple object of same time through JPA.
Here is my code for reference
#Override
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public Boolean changeDplListMappingByCustomWatchList(List<Integer> dplIds, Integer customWatchListId,
ServiceRequestor customServiceRequestor) {
for(Integer dplId : dplIds) {
if(dplId != null) {
CustomWatchListDplMapping customWatchListDplMapping = new CustomWatchListDplMapping();
customWatchListDplMapping.setDplId(dplId);
customWatchListDplMapping.setWatchListId(customWatchListId);
this.create(customWatchListDplMapping);
}
}
}
catch(Exception e) {
LOG.error("Exception occured while changing dpl mapping by custom watchList id", e);
}
return true;
}
public void create(Model entity) {
manager.persist(entity);
manager.joinTransaction();
}
After first entity when it iterate through second one it throws an exception. If it has only one entity to save then it works well, but for more than one entity model it throws this exception.
by default pessimistic lock is for 1 second so please do the changes in the properties file it will help you to unlock and you will be able to save into database
I am using following code to iterate through a database's documents.
public void readDataStore() throws IOException {
Query query = document_datastore.find(DocumentPojo.class);
List<DocumentPojo> documentPojos = query.asList();
documentPojos.forEach(obj -> {
try {
System.out.println(obj.getDocid());
} catch (IOException e) {
e.printStackTrace();
}
}
);
}
Currently the DB has not more than 100 documents but in future it can have ~100000 documents. I suspect then it may run into performance issues?
DocumentPojo is the class that I map the results to. I am using Java 8 and Morphia.
How should I solve this issue?
Use query.fetch() to get the MorphiaIterator and then handle each document as you get it. It won't pull them all in to memory at once allowing you to process your hundred thousand+ documents.
One way of implementing #evanchooly's answer:
public void readDataStore() throws IOException {
final Query query = document_datastore.find(DocumentPojo.class);
query.fetch().forEach(obj -> {
try {
System.out.println(obj.getDocid());
} catch (final IOException e) {
e.printStackTrace();
}
});
}
In my web application I'm using Stateless sessions with Hibernate to have better performances on my inserts and updates.
It was working fine with H2 database (the one used in play framework in dev mode).
But when I test it with MySQL I get the following exception :
ERROR ~ Lock wait timeout exceeded; try restarting transaction
ERROR ~ HHH000315: Exception executing batch [Lock wait timeout exceeded; try restarting transaction]
Here is the code :
public static void update() {
Session session = (Session) JPA.em().getDelegate();
StatelessSession stateless = this.session.getSessionFactory().openStatelessSession();
try {
stateless.beginTransaction();
// Fetch all products
{
List<ProductType> list = ProductType.retrieveAllWithHistory();
for (ProductType pt : list) {
updatePrice(pt, stateless);
}
}
// Fetch all raw materials
{
List<RawMaterialType> list = RawMaterialType.retrieveAllWithHistory();
for (RawMaterialType rm : list) {
updatePrice(rm, stateless);
}
}
} catch (Exception ex) {
play.Logger.error(ex.getMessage());
ExceptionLog.log(ex, Thread.currentThread());
} finally {
stateless.getTransaction().commit();
stateless.close();
}
}
private static void updatePrice(ProductType pt, StatelessSession stateless) {
pt.priceDelta = computeDelta();
pt.unitPrice = computePrice();
stateless.update(pt);
PriceHistory ph = new PriceHistory(pt, price);
stateless.insert(ph);
}
private static void updatePrice(RawMaterialType rm, StatelessSession stateless) {
rm.priceDelta = computeDelta();
rm.unitPrice = computePrice();
stateless.update(rm);
PriceHistory ph = new GoodPriceHistory(rm, price);
stateless.insert(ph);
}
In this example I have 3 simple Entities (ProductType, RawMaterialType and PriceHistory).
computeDelta and computePrice are just algorithm functions with no DB stuff.
retrieveAllWithHistory functions are functions that fetch some data from the database using Play framework model functions.
So, this code retrieves some data, edit some, create new one and finally save everything.
Why have I a lock exception with MySQL and no exception with H2 ?
I'm not sure why you have a commit in a finally block. Give this structure a try:
try {
factory.getCurrentSession().beginTransaction();
factory.getCurrentSession().getTransaction().commit();
} catch (RuntimeException e) {
factory.getCurrentSession().getTransaction().rollback();
throw e; // or display error message
}
Also, it might be helpful for you to check this documentation.