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 <55171818+quantranhong1...@users.noreply.github.com>
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: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to