Compare commits

..

2 Commits

Author SHA1 Message Date
12326fe122 Merge pull request 'fix: ws timeout' (#93) from dev into master
Reviewed-on: #93
2026-01-02 21:33:40 +00:00
9624ff93f0 fix: ws timeout
All checks were successful
Deploy Production Server / if_merged (pull_request) Successful in 6m42s
2026-01-02 22:33:11 +01:00
9 changed files with 98 additions and 63 deletions

View File

@ -16,6 +16,7 @@ import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.unchecked.Unchecked;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.ws.rs.ForbiddenException;
import org.jboss.logging.Logger;
@ -23,6 +24,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.Executor;
import static fr.titionfire.ffsaf.net2.Client_Thread.MAPPER;
@ -40,7 +42,7 @@ public class CompetitionWS {
RCategorie rCategorie;
@Inject
RRegister rRegister;
RRegister rRegister;
@Inject
RCardboard rCardboard;
@ -51,6 +53,15 @@ public class CompetitionWS {
@Inject
CompetPermService competPermService;
@SuppressWarnings("CdiInjectionPointsInspection")
@Inject
OpenConnections connections;
@Inject
@Named("notify-executor")
Executor notifyExecutor;
private static Executor executor;
@Inject
CompetitionRepository competitionRepository;
@ -79,6 +90,8 @@ public class CompetitionWS {
getWSReceiverMethods(RCategorie.class, rCategorie);
getWSReceiverMethods(RRegister.class, rRegister);
getWSReceiverMethods(RCardboard.class, rCardboard);
executor = notifyExecutor;
}
@OnOpen
@ -186,7 +199,7 @@ public class CompetitionWS {
// return Uni.createFrom().item(new Message<>(message.uuid(), message.code(), MessageType.REPLY, "ko"));
}
public static Uni<Void> sendNotifyToOtherEditor(WebSocketConnection connection, String code, Object data) {
public static void sendNotifyToOtherEditor(WebSocketConnection connection, String code, Object data) {
String uuid = connection.pathParam("uuid");
List<Uni<Void>> queue = new ArrayList<>();
@ -198,7 +211,15 @@ public class CompetitionWS {
}
});
return Uni.join().all(queue).andCollectFailures().onFailure().recoverWithNull().replaceWithVoid();
Uni.join().all(queue)
.andCollectFailures()
.runSubscriptionOn(executor)
.subscribeAsCompletionStage()
.whenComplete((v, t) -> {
if (t != null) {
LOGGER.error("Error sending ws_out message", t);
}
});
}
@OnError

View File

@ -0,0 +1,19 @@
package fr.titionfire.ffsaf.ws;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Singleton
public class ExecutorProducer {
@Produces
@Named("notify-executor")
public Executor produceNotifyExecutor() {
// Créez un pool de threads avec une taille fixe (par exemple, 10 threads)
return Executors.newFixedThreadPool(10);
}
}

View File

@ -80,35 +80,32 @@ public class RCardboard {
return Uni.createFrom().nullItem();
return Panache.withTransaction(() -> cardboardRepository.persist(cardboardModel));
}))
.call(model -> SSCardboard.sendCardboard(connection, CardboardEntity.fromModel(model)))
.invoke(model -> SSCardboard.sendCardboard(connection, CardboardEntity.fromModel(model)))
.replaceWithVoid();
}
@WSReceiver(code = "getCardboardWithoutThis", permission = PermLevel.VIEW)
public Uni<CardboardAllMatch> getCardboardWithoutThis(WebSocketConnection connection, Long matchId) {
return getById(matchId, connection)
.chain(matchModel -> cardboardRepository.list("compet = ?1 AND match != ?2", matchModel.getCategory().getCompet(), matchModel)
.chain(matchModel -> cardboardRepository.list("compet = ?1 AND match != ?2",
matchModel.getCategory().getCompet(), matchModel)
.map(models -> {
CardboardAllMatch out = new CardboardAllMatch();
models.stream().filter(c -> (matchModel.getC1_id() != null
&& Objects.equals(c.getComb(), matchModel.getC1_id()))
|| (matchModel.getC1_guest() != null
&& Objects.equals(c.getGuestComb(), matchModel.getC1_guest())))
.forEach(c -> {
out.c1_yellow += c.getYellow();
out.c1_red += c.getRed();
});
models.stream().filter(c -> (matchModel.getC2_id() != null
&& Objects.equals(c.getComb(), matchModel.getC2_id()))
|| (matchModel.getC2_guest() != null
&& Objects.equals(c.getGuestComb(), matchModel.getC2_guest())))
.forEach(c -> {
out.c2_yellow += c.getYellow();
out.c2_red += c.getRed();
});
for (CardboardModel c : models) {
if ((matchModel.getC1_id() != null && Objects.equals(c.getComb(),
matchModel.getC1_id())) || (matchModel.getC1_guest() != null && Objects.equals(
c.getGuestComb(), matchModel.getC1_guest()))) {
out.c1_yellow += c.getYellow();
out.c1_red += c.getRed();
}
if ((matchModel.getC2_id() != null && Objects.equals(c.getComb(),
matchModel.getC2_id())) || (matchModel.getC2_guest() != null && Objects.equals(
c.getGuestComb(), matchModel.getC2_guest()))) {
out.c2_yellow += c.getYellow();
out.c2_red += c.getRed();
}
}
return out;
}));
}

View File

@ -97,7 +97,7 @@ public class RCategorie {
return categoryRepository.create(categoryModel);
})
.call(cat -> SSCategorie.sendAddCategory(connection, cat))
.invoke(cat -> SSCategorie.sendAddCategory(connection, cat))
.map(CategoryModel::getId);
}
@ -121,7 +121,7 @@ public class RCategorie {
Uni<Long> finalUni = uni;
return Panache.withTransaction(() -> finalUni);
})
.call(cat -> SSCategorie.sendCategory(connection, cat))
.invoke(cat -> SSCategorie.sendCategory(connection, cat))
.replaceWithVoid();
}
@ -202,7 +202,7 @@ public class RCategorie {
.call(__ -> treeRepository.flush())
.call(cat -> treeRepository.list("category = ?1 AND level != 0", cat.getId())
.map(treeModels -> treeModels.stream().map(TreeEntity::fromModel).toList())
.chain(trees -> SSCategorie.sendTreeCategory(connection, trees)))
.invoke(trees -> SSCategorie.sendTreeCategory(connection, trees)))
.replaceWithVoid();
}
@ -212,7 +212,7 @@ public class RCategorie {
.call(cat -> Panache.withTransaction(() -> treeRepository.delete("category = ?1", cat.getId())
.call(__ -> matchRepository.delete("category = ?1", cat))))
.chain(cat -> Panache.withTransaction(() -> categoryRepository.delete(cat)))
.call(__ -> SSCategorie.sendDelCategory(connection, id))
.invoke(__ -> SSCategorie.sendDelCategory(connection, id))
.replaceWithVoid();
}

View File

@ -84,7 +84,7 @@ public class RMatch {
}))
.chain(categoryModel -> creatMatch(categoryModel, m))
.chain(mm -> Panache.withTransaction(() -> matchRepository.create(mm)))
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.invoke(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.replaceWithVoid();
}
@ -121,7 +121,7 @@ public class RMatch {
mm.setC2_guest(null);
}))
.chain(mm -> Panache.withTransaction(() -> matchRepository.persist(mm)))
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.invoke(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.replaceWithVoid();
}
@ -136,7 +136,7 @@ public class RMatch {
m.getCategory_ord()))
.invoke(m -> m.setCategory_ord(order.pos))
.call(m -> Panache.withTransaction(() -> matchRepository.persist(m)))
.call(mm -> SSMatch.sendMatchOrder(connection, order))
.invoke(mm -> SSMatch.sendMatchOrder(connection, order))
.replaceWithVoid();
}
@ -163,12 +163,12 @@ public class RMatch {
.call(mm -> {
if (mm.isEnd() && mm.win() != old_win && mm.getCategory_ord() == -42) {
return updateEndAndTree(mm, new ArrayList<>())
.call(l -> SSMatch.sendMatch(connection, l));
.invoke(l -> SSMatch.sendMatch(connection, l));
}
return Uni.createFrom().nullItem();
});
})
.call(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.invoke(mm -> SSMatch.sendMatch(connection, MatchEntity.fromModel(mm)))
.replaceWithVoid();
}
@ -189,7 +189,7 @@ public class RMatch {
})
.invoke(mm -> toSend.add(MatchEntity.fromModel(mm)))
.chain(mm -> updateEndAndTree(mm, toSend))
.call(__ -> SSMatch.sendMatch(connection, toSend))
.invoke(__ -> SSMatch.sendMatch(connection, toSend))
.replaceWithVoid();
}
@ -278,7 +278,7 @@ public class RMatch {
public Uni<Void> deleteMatch(WebSocketConnection connection, Long idMatch) {
return getById(idMatch, connection)
.chain(matchModel -> Panache.withTransaction(() -> matchRepository.delete(matchModel)))
.call(__ -> SSMatch.sendDeleteMatch(connection, idMatch))
.invoke(__ -> SSMatch.sendDeleteMatch(connection, idMatch))
.replaceWithVoid();
}
@ -295,7 +295,7 @@ public class RMatch {
.call(cm -> data.matchesToRemove.isEmpty() ? Uni.createFrom().voidItem() :
(Panache.withTransaction(
() -> matchRepository.delete("id IN ?1 AND category = ?2", data.matchesToRemove, cm))
.call(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove))))
.invoke(__ -> SSMatch.sendDeleteMatch(connection, data.matchesToRemove))))
.call(cm -> Panache.withSession(() -> matchRepository.list("id IN ?1 AND category = ?2",
Stream.concat(data.matchOrderToUpdate.keySet().stream(),
data.matchPouleToUpdate.keySet().stream())
@ -321,7 +321,7 @@ public class RMatch {
.chain(mm -> mm.isEmpty() ? Uni.createFrom().voidItem() :
Panache.withTransaction(() -> matchRepository.create(mm))
.invoke(__ -> matches.addAll(mm.stream().map(MatchEntity::fromModel).toList())))
.call(__ -> SSMatch.sendMatch(connection, matches))
.invoke(__ -> SSMatch.sendMatch(connection, matches))
.replaceWithVoid();
}

View File

@ -3,11 +3,10 @@ package fr.titionfire.ffsaf.ws.send;
import fr.titionfire.ffsaf.domain.entity.CardboardEntity;
import fr.titionfire.ffsaf.ws.CompetitionWS;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
public class SSCardboard {
public static Uni<Void> sendCardboard(WebSocketConnection connection, CardboardEntity cardboardEntity) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendCardboard", cardboardEntity);
public static void sendCardboard(WebSocketConnection connection, CardboardEntity cardboardEntity) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendCardboard", cardboardEntity);
}
}

View File

@ -5,33 +5,32 @@ import fr.titionfire.ffsaf.domain.entity.TreeEntity;
import fr.titionfire.ffsaf.ws.CompetitionWS;
import fr.titionfire.ffsaf.ws.recv.RCategorie;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import java.util.List;
public class SSCategorie {
public static Uni<Void> sendAddCategory(WebSocketConnection connection, CategoryModel category) {
return SSCategorie.sendAddCategory(connection, RCategorie.JustCategorie.from(category));
public static void sendAddCategory(WebSocketConnection connection, CategoryModel category) {
SSCategorie.sendAddCategory(connection, RCategorie.JustCategorie.from(category));
}
public static Uni<Void> sendAddCategory(WebSocketConnection connection, RCategorie.JustCategorie justCategorie) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendAddCategory", justCategorie);
public static void sendAddCategory(WebSocketConnection connection, RCategorie.JustCategorie justCategorie) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendAddCategory", justCategorie);
}
public static Uni<Void> sendCategory(WebSocketConnection connection, CategoryModel category) {
return SSCategorie.sendCategory(connection, RCategorie.JustCategorie.from(category));
public static void sendCategory(WebSocketConnection connection, CategoryModel category) {
SSCategorie.sendCategory(connection, RCategorie.JustCategorie.from(category));
}
public static Uni<Void> sendCategory(WebSocketConnection connection, RCategorie.JustCategorie justCategorie) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendCategory", justCategorie);
public static void sendCategory(WebSocketConnection connection, RCategorie.JustCategorie justCategorie) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendCategory", justCategorie);
}
public static Uni<Void> sendTreeCategory(WebSocketConnection connection, List<TreeEntity> treeEntities) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendTreeCategory", treeEntities);
public static void sendTreeCategory(WebSocketConnection connection, List<TreeEntity> treeEntities) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendTreeCategory", treeEntities);
}
public static Uni<?> sendDelCategory(WebSocketConnection connection, Long id) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendDelCategory", id);
public static void sendDelCategory(WebSocketConnection connection, Long id) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendDelCategory", id);
}
}

View File

@ -4,29 +4,28 @@ import fr.titionfire.ffsaf.domain.entity.MatchEntity;
import fr.titionfire.ffsaf.ws.CompetitionWS;
import fr.titionfire.ffsaf.ws.recv.RMatch;
import io.quarkus.websockets.next.WebSocketConnection;
import io.smallrye.mutiny.Uni;
import java.util.List;
public class SSMatch {
public static Uni<Void> sendMatch(WebSocketConnection connection, MatchEntity matchEntity) {
return SSMatch.sendMatch(connection, List.of(matchEntity));
public static void sendMatch(WebSocketConnection connection, MatchEntity matchEntity) {
SSMatch.sendMatch(connection, List.of(matchEntity));
}
public static Uni<Void> sendMatch(WebSocketConnection connection, List<MatchEntity> matchEntities) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendMatch", matchEntities);
public static void sendMatch(WebSocketConnection connection, List<MatchEntity> matchEntities) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendMatch", matchEntities);
}
public static Uni<Void> sendMatchOrder(WebSocketConnection connection, RMatch.MatchOrder matchOrder) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendMatchOrder", matchOrder);
public static void sendMatchOrder(WebSocketConnection connection, RMatch.MatchOrder matchOrder) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendMatchOrder", matchOrder);
}
public static Uni<Void> sendDeleteMatch(WebSocketConnection connection, Long l) {
return SSMatch.sendDeleteMatch(connection, List.of(l));
public static void sendDeleteMatch(WebSocketConnection connection, Long l) {
SSMatch.sendDeleteMatch(connection, List.of(l));
}
public static Uni<Void> sendDeleteMatch(WebSocketConnection connection, List<Long> longs) {
return CompetitionWS.sendNotifyToOtherEditor(connection, "sendDeleteMatch", longs);
public static void sendDeleteMatch(WebSocketConnection connection, List<Long> longs) {
CompetitionWS.sendNotifyToOtherEditor(connection, "sendDeleteMatch", longs);
}
}

View File

@ -8,6 +8,7 @@ quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.physical-naming-strategy=fr.titionfire.ffsaf.data.SafcaNamingStrategy
quarkus.hibernate-orm.dialect=fr.titionfire.ffsaf.data.CustomPostgreSQLDialect
quarkus.datasource.jdbc.reactive.max-size=40
quarkus.http.cors.enabled=true
quarkus.quartz.start-mode=forced