GH-8760 Postgres: using DELETE ... RETURNING
Fixes https://github.com/spring-projects/spring-integration/issues/8760 * Make `PostgresChannelMessageStoreQueryProvider` to use single `DELETE ... RETURNING` for polling statements * Add `isUsingSingleStatementForPoll` and use it from `JdbcChannelMessageStore` * Execute Postgres init scripts to `PostgresContainerTest` * Code clean up * Document the new feature
This commit is contained in:
committed by
Artem Bilan
parent
29186e2b46
commit
87a2ac5b5c
@@ -65,7 +65,7 @@ import org.springframework.util.StringUtils;
|
||||
* Channel-specific implementation of
|
||||
* {@link org.springframework.integration.store.BasicMessageGroupStore} using a relational
|
||||
* database via JDBC.
|
||||
*
|
||||
* <p>
|
||||
* This message store shall be used for message channels only.
|
||||
* <p>
|
||||
* As such, the {@link JdbcChannelMessageStore} uses database specific SQL queries.
|
||||
@@ -86,6 +86,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Gary Russell
|
||||
* @author Meherzad Lahewala
|
||||
* @author Trung Pham
|
||||
* @author Johannes Edmeier
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
@@ -554,12 +555,16 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
public Message<?> pollMessageFromGroup(Object groupId) {
|
||||
String key = getKey(groupId);
|
||||
Message<?> polledMessage = doPollForMessage(key);
|
||||
if (polledMessage != null && !doRemoveMessageFromGroup(groupId, polledMessage)) {
|
||||
if (polledMessage != null && !isSingleStatementForPoll() && !doRemoveMessageFromGroup(groupId, polledMessage)) {
|
||||
return null;
|
||||
}
|
||||
return polledMessage;
|
||||
}
|
||||
|
||||
private boolean isSingleStatementForPoll() {
|
||||
return this.channelMessageStoreQueryProvider.isSingleStatementForPoll();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method executes a call to the DB to get the oldest Message in the
|
||||
* MessageGroup which in the context of the {@link JdbcChannelMessageStore}
|
||||
|
||||
@@ -25,6 +25,7 @@ package org.springframework.integration.jdbc.store.channel;
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @author Adama Sorho
|
||||
* @author Johannes Edmeier
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
@@ -33,7 +34,7 @@ public interface ChannelMessageStoreQueryProvider {
|
||||
String SELECT_COMMON = """
|
||||
SELECT %PREFIX%CHANNEL_MESSAGE.MESSAGE_ID, %PREFIX%CHANNEL_MESSAGE.MESSAGE_BYTES
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where %PREFIX%CHANNEL_MESSAGE.GROUP_KEY = :group_key and %PREFIX%CHANNEL_MESSAGE.REGION = :region\s
|
||||
where %PREFIX%CHANNEL_MESSAGE.GROUP_KEY = :group_key and %PREFIX%CHANNEL_MESSAGE.REGION = :region
|
||||
""";
|
||||
|
||||
/**
|
||||
@@ -125,4 +126,14 @@ public interface ChannelMessageStoreQueryProvider {
|
||||
*/
|
||||
String getPriorityPollFromGroupQuery();
|
||||
|
||||
/**
|
||||
* Indicate if the queries for polling are using a single statement (e.g. DELETE ... RETURNING) to
|
||||
* retrieve and delete the message from the channel store.
|
||||
* @return true if a single statement is used, false if a select and delete is required.
|
||||
* @since 6.2
|
||||
*/
|
||||
default boolean isSingleStatementForPoll() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.springframework.integration.jdbc.store.channel;
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Adama Sorho
|
||||
* @author Johannes Edmeier
|
||||
*
|
||||
* @since 2.2
|
||||
*/
|
||||
@@ -27,30 +28,69 @@ public class PostgresChannelMessageStoreQueryProvider implements ChannelMessageS
|
||||
|
||||
@Override
|
||||
public String getPollFromGroupExcludeIdsQuery() {
|
||||
return SELECT_COMMON
|
||||
+ "and %PREFIX%CHANNEL_MESSAGE.MESSAGE_ID not in (:message_ids) "
|
||||
+ "order by CREATED_DATE, MESSAGE_SEQUENCE LIMIT 1 FOR UPDATE SKIP LOCKED";
|
||||
return """
|
||||
delete
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where CTID = (select CTID
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where %PREFIX%CHANNEL_MESSAGE.GROUP_KEY = :group_key
|
||||
and %PREFIX%CHANNEL_MESSAGE.REGION = :region
|
||||
and %PREFIX%CHANNEL_MESSAGE.MESSAGE_ID not in (:message_ids)
|
||||
order by CREATED_DATE, MESSAGE_SEQUENCE
|
||||
limit 1 for update skip locked)
|
||||
returning MESSAGE_ID, MESSAGE_BYTES;
|
||||
""";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPollFromGroupQuery() {
|
||||
return SELECT_COMMON +
|
||||
"order by CREATED_DATE, MESSAGE_SEQUENCE LIMIT 1 FOR UPDATE SKIP LOCKED";
|
||||
return """
|
||||
delete
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where CTID = (select CTID
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where %PREFIX%CHANNEL_MESSAGE.GROUP_KEY = :group_key
|
||||
and %PREFIX%CHANNEL_MESSAGE.REGION = :region
|
||||
order by CREATED_DATE, MESSAGE_SEQUENCE
|
||||
limit 1 for update skip locked)
|
||||
returning MESSAGE_ID, MESSAGE_BYTES;
|
||||
""";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPriorityPollFromGroupExcludeIdsQuery() {
|
||||
return SELECT_COMMON +
|
||||
"and %PREFIX%CHANNEL_MESSAGE.MESSAGE_ID not in (:message_ids) " +
|
||||
"order by MESSAGE_PRIORITY DESC NULLS LAST, CREATED_DATE, MESSAGE_SEQUENCE " +
|
||||
"LIMIT 1 FOR UPDATE SKIP LOCKED";
|
||||
return """
|
||||
delete
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where CTID = (select CTID
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where %PREFIX%CHANNEL_MESSAGE.GROUP_KEY = :group_key
|
||||
and %PREFIX%CHANNEL_MESSAGE.REGION = :region
|
||||
and %PREFIX%CHANNEL_MESSAGE.MESSAGE_ID not in (:message_ids)
|
||||
order by MESSAGE_PRIORITY DESC NULLS LAST, CREATED_DATE, MESSAGE_SEQUENCE
|
||||
limit 1 for update skip locked)
|
||||
returning MESSAGE_ID, MESSAGE_BYTES;
|
||||
""";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPriorityPollFromGroupQuery() {
|
||||
return SELECT_COMMON +
|
||||
"order by MESSAGE_PRIORITY DESC NULLS LAST, CREATED_DATE, MESSAGE_SEQUENCE " +
|
||||
"LIMIT 1 FOR UPDATE SKIP LOCKED";
|
||||
return """
|
||||
delete
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where CTID = (select CTID
|
||||
from %PREFIX%CHANNEL_MESSAGE
|
||||
where %PREFIX%CHANNEL_MESSAGE.GROUP_KEY = :group_key
|
||||
and %PREFIX%CHANNEL_MESSAGE.REGION = :region
|
||||
order by MESSAGE_PRIORITY DESC NULLS LAST, CREATED_DATE, MESSAGE_SEQUENCE
|
||||
limit 1 for update skip locked)
|
||||
returning MESSAGE_ID, MESSAGE_BYTES;
|
||||
""";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleStatementForPoll() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
-- Autogenerated: do not edit this file
|
||||
|
||||
DROP INDEX INT_MESSAGE_IX1 ;
|
||||
DROP INDEX INT_CHANNEL_MSG_DATE_IDX ;
|
||||
DROP INDEX INT_CHANNEL_MSG_PRIORITY_IDX ;
|
||||
DROP TABLE INT_MESSAGE ;
|
||||
DROP TABLE INT_MESSAGE_GROUP ;
|
||||
DROP TABLE INT_GROUP_TO_MESSAGE ;
|
||||
DROP TABLE INT_LOCK ;
|
||||
DROP TABLE INT_CHANNEL_MESSAGE ;
|
||||
DROP TABLE INT_METADATA_STORE ;
|
||||
DROP SEQUENCE INT_MESSAGE_SEQ ;
|
||||
DROP INDEX IF EXISTS INT_MESSAGE_IX1 ;
|
||||
DROP INDEX IF EXISTS INT_CHANNEL_MSG_DATE_IDX ;
|
||||
DROP INDEX IF EXISTS INT_CHANNEL_MSG_PRIORITY_IDX ;
|
||||
DROP TABLE IF EXISTS INT_MESSAGE ;
|
||||
DROP TABLE IF EXISTS INT_MESSAGE_GROUP ;
|
||||
DROP TABLE IF EXISTS INT_GROUP_TO_MESSAGE ;
|
||||
DROP TABLE IF EXISTS INT_LOCK ;
|
||||
DROP TABLE IF EXISTS INT_CHANNEL_MESSAGE ;
|
||||
DROP TABLE IF EXISTS INT_METADATA_STORE ;
|
||||
DROP SEQUENCE IF EXISTS INT_MESSAGE_SEQ ;
|
||||
|
||||
Reference in New Issue
Block a user