This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch postgresql
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/postgresql by this push:
new 29c85f3fe2 JAMES 2586 PostgresPushSubscriptionRepository: rely on
Postgres unique constraint for deviceClientId (#2094)
29c85f3fe2 is described below
commit 29c85f3fe23b9d8638fab0505bc839f981b40845
Author: Trần Hồng Quân <[email protected]>
AuthorDate: Mon Mar 11 03:23:14 2024 +0700
JAMES 2586 PostgresPushSubscriptionRepository: rely on Postgres unique
constraint for deviceClientId (#2094)
Avoid 2 round trips (checking duplicate deviceClientId + INSERT
subscription) when saving a subscription.
---
.../PostgresPushSubscriptionDAO.java | 17 ++++++-------
.../PostgresPushSubscriptionModule.java | 8 ++++---
.../PostgresPushSubscriptionRepository.java | 28 ++++++++++++----------
3 files changed, 29 insertions(+), 24 deletions(-)
diff --git
a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionDAO.java
b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionDAO.java
index 5d59e08fe2..91b06c248b 100644
---
a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionDAO.java
+++
b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionDAO.java
@@ -21,6 +21,8 @@ package org.apache.james.jmap.postgres.pushsubscription;
import static
org.apache.james.backends.postgres.PostgresCommons.IN_CLAUSE_MAX_SIZE;
import static
org.apache.james.backends.postgres.PostgresCommons.OFFSET_DATE_TIME_ZONED_DATE_TIME_FUNCTION;
+import static
org.apache.james.backends.postgres.utils.PostgresUtils.UNIQUE_CONSTRAINT_VIOLATION_PREDICATE;
+import static
org.apache.james.jmap.postgres.pushsubscription.PostgresPushSubscriptionModule.PushSubscriptionTable.PRIMARY_KEY_CONSTRAINT;
import java.time.ZonedDateTime;
import java.util.Arrays;
@@ -28,11 +30,13 @@ import java.util.Collection;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.james.backends.postgres.utils.PostgresExecutor;
import org.apache.james.core.Username;
import org.apache.james.jmap.api.change.TypeStateFactory;
+import org.apache.james.jmap.api.model.DeviceClientIdInvalidException;
import org.apache.james.jmap.api.model.PushSubscription;
import org.apache.james.jmap.api.model.PushSubscriptionExpiredTime;
import org.apache.james.jmap.api.model.PushSubscriptionId;
@@ -51,6 +55,8 @@ import scala.jdk.javaapi.CollectionConverters;
import scala.jdk.javaapi.OptionConverters;
public class PostgresPushSubscriptionDAO {
+ private static final Predicate<Throwable> IS_PRIMARY_KEY_UNIQUE_CONSTRAINT
= throwable -> throwable.getMessage().contains(PRIMARY_KEY_CONSTRAINT);
+
private final PostgresExecutor postgresExecutor;
private final TypeStateFactory typeStateFactory;
@@ -71,7 +77,9 @@ public class PostgresPushSubscriptionDAO {
.set(PushSubscriptionTable.VERIFICATION_CODE,
pushSubscription.verificationCode())
.set(PushSubscriptionTable.VALIDATED, pushSubscription.validated())
.set(PushSubscriptionTable.ENCRYPT_PUBLIC_KEY,
OptionConverters.toJava(pushSubscription.keys().map(PushSubscriptionKeys::p256dh)).orElse(null))
- .set(PushSubscriptionTable.ENCRYPT_AUTH_SECRET,
OptionConverters.toJava(pushSubscription.keys().map(PushSubscriptionKeys::auth)).orElse(null))));
+ .set(PushSubscriptionTable.ENCRYPT_AUTH_SECRET,
OptionConverters.toJava(pushSubscription.keys().map(PushSubscriptionKeys::auth)).orElse(null))))
+
.onErrorMap(UNIQUE_CONSTRAINT_VIOLATION_PREDICATE.and(IS_PRIMARY_KEY_UNIQUE_CONSTRAINT),
+ e -> new
DeviceClientIdInvalidException(pushSubscription.deviceClientId(),
"deviceClientId must be unique"));
}
public Flux<PushSubscription> listByUsername(Username username) {
@@ -134,13 +142,6 @@ public class PostgresPushSubscriptionDAO {
.map(record ->
OFFSET_DATE_TIME_ZONED_DATE_TIME_FUNCTION.apply(record.get(PushSubscriptionTable.EXPIRES)));
}
- public Mono<Boolean> existDeviceClientId(Username username, String
deviceClientId) {
- return postgresExecutor.executeExists(dslContext ->
dslContext.selectOne()
- .from(PushSubscriptionTable.TABLE_NAME)
- .where(PushSubscriptionTable.USER.eq(username.asString()))
- .and(PushSubscriptionTable.DEVICE_CLIENT_ID.eq(deviceClientId)));
- }
-
private PushSubscription recordAsPushSubscription(Record record) {
try {
return new PushSubscription(new
PushSubscriptionId(record.get(PushSubscriptionTable.ID)),
diff --git
a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionModule.java
b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionModule.java
index 4a16bed563..ebe3c552ee 100644
---
a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionModule.java
+++
b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionModule.java
@@ -36,13 +36,14 @@ public interface PostgresPushSubscriptionModule {
interface PushSubscriptionTable {
Table<Record> TABLE_NAME = DSL.table("push_subscription");
+
+ String PRIMARY_KEY_CONSTRAINT =
"push_subscription_primary_key_constraint";
+
Field<String> USER = DSL.field("username",
SQLDataType.VARCHAR.notNull());
Field<String> DEVICE_CLIENT_ID = DSL.field("device_client_id",
SQLDataType.VARCHAR.notNull());
-
Field<UUID> ID = DSL.field("id", SQLDataType.UUID.notNull());
Field<OffsetDateTime> EXPIRES = DSL.field("expires",
PostgresCommons.DataTypes.TIMESTAMP_WITH_TIMEZONE);
Field<String[]> TYPES = DSL.field("types",
PostgresCommons.DataTypes.STRING_ARRAY.notNull());
-
Field<String> URL = DSL.field("url", SQLDataType.VARCHAR.notNull());
Field<String> VERIFICATION_CODE = DSL.field("verification_code",
SQLDataType.VARCHAR);
Field<String> ENCRYPT_PUBLIC_KEY = DSL.field("encrypt_public_key",
SQLDataType.VARCHAR);
@@ -61,7 +62,8 @@ public interface PostgresPushSubscriptionModule {
.column(ENCRYPT_PUBLIC_KEY)
.column(ENCRYPT_AUTH_SECRET)
.column(VALIDATED)
- .primaryKey(USER, DEVICE_CLIENT_ID)))
+ .constraint(DSL.constraint(PRIMARY_KEY_CONSTRAINT)
+ .primaryKey(USER, DEVICE_CLIENT_ID))))
.supportsRowLevelSecurity()
.build();
diff --git
a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionRepository.java
b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionRepository.java
index c2370e43ad..4f81c8237d 100644
---
a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionRepository.java
+++
b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/pushsubscription/PostgresPushSubscriptionRepository.java
@@ -34,7 +34,6 @@ import javax.inject.Singleton;
import org.apache.james.backends.postgres.utils.PostgresExecutor;
import org.apache.james.core.Username;
import org.apache.james.jmap.api.change.TypeStateFactory;
-import org.apache.james.jmap.api.model.DeviceClientIdInvalidException;
import org.apache.james.jmap.api.model.ExpireTimeInvalidException;
import org.apache.james.jmap.api.model.InvalidPushSubscriptionKeys;
import org.apache.james.jmap.api.model.PushSubscription;
@@ -64,26 +63,29 @@ public class PostgresPushSubscriptionRepository implements
PushSubscriptionRepos
@Override
public Mono<PushSubscription> save(Username username,
PushSubscriptionCreationRequest request) {
- PushSubscription pushSubscription = PushSubscription.from(request,
-
evaluateExpiresTime(OptionConverters.toJava(request.expires().map(PushSubscriptionExpiredTime::value)),
clock));
-
PostgresPushSubscriptionDAO pushSubscriptionDAO = getDAO(username);
- return pushSubscriptionDAO.existDeviceClientId(username,
request.deviceClientId())
- .handle((isDuplicated, sink) -> {
+
+ return validateCreationRequest(request)
+ .then(Mono.defer(() -> {
+ PushSubscription pushSubscription =
PushSubscription.from(request,
+
evaluateExpiresTime(OptionConverters.toJava(request.expires().map(PushSubscriptionExpiredTime::value)),
clock));
+
+ return pushSubscriptionDAO.save(username, pushSubscription)
+ .thenReturn(pushSubscription);
+ }));
+ }
+
+ private Mono<Object>
validateCreationRequest(PushSubscriptionCreationRequest request) {
+ return Mono.just(request)
+ .handle((creationRequest, sink) -> {
if (isInThePast(request.expires(), clock)) {
sink.error(new
ExpireTimeInvalidException(request.expires().get().value(), "expires must be
greater than now"));
return;
}
- if (isDuplicated) {
- sink.error(new
DeviceClientIdInvalidException(request.deviceClientId(), "deviceClientId must
be unique"));
- return;
- }
if (isInvalidPushSubscriptionKey(request.keys())) {
sink.error(new
InvalidPushSubscriptionKeys(request.keys().get()));
}
- })
- .then(Mono.defer(() -> pushSubscriptionDAO.save(username,
pushSubscription))
- .thenReturn(pushSubscription));
+ });
}
@Override
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]