Skip to content

Commit 34ed28d

Browse files
authored
Merge pull request #745 from apache/master
[pull] master from apache:master
2 parents ccaa926 + 0ae050f commit 34ed28d

2 files changed

Lines changed: 75 additions & 2 deletions

File tree

server/task/task-distributed/src/main/java/org/apache/james/task/eventsourcing/distributed/RabbitMQWorkQueue.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ private Mono<Task.Result> executeTask(AcknowledgableDelivery delivery) {
189189
.flatMap(taskId -> Mono.fromCallable(() -> new String(delivery.getBody(), StandardCharsets.UTF_8))
190190
.flatMap(bodyValue -> deserialize(bodyValue, taskId))
191191
.doOnNext(task -> delivery.ack())
192-
.flatMap(task -> executeOnWorker(taskId, task)))
192+
.flatMap(task -> executeOnWorker(taskId, task))
193+
.doOnSuccess(result -> LOGGER.info("Executed task {} yield {}", taskId, result)))
193194
.onErrorResume(error -> {
194195
Optional<Object> taskId = Optional.ofNullable(delivery.getProperties())
195196
.flatMap(props -> Optional.ofNullable(props.getHeaders()))
@@ -211,6 +212,7 @@ private Mono<Task> deserialize(String json, TaskId taskId) {
211212
}
212213

213214
private Mono<Task.Result> executeOnWorker(TaskId taskId, Task task) {
215+
LOGGER.info("Executing task {} ({}) ", taskId, task.getClass());
214216
return worker.executeTask(new TaskWithId(taskId, task))
215217
.timeout(rabbitMQConfiguration.getTaskQueueConsumerTimeout())
216218
.onErrorResume(error -> {
@@ -274,14 +276,22 @@ public void submit(TaskWithId taskWithId) {
274276
.build();
275277

276278
OutboundMessage outboundMessage = new OutboundMessage(EXCHANGE_NAME, ROUTING_KEY, basicProperties, payload);
277-
sender.send(Mono.just(outboundMessage)).block();
279+
sender.send(Mono.just(outboundMessage))
280+
.onErrorResume(e -> {
281+
LOGGER.error("Publishing task {} failed", taskWithId.getId(), e);
282+
return Mono.from(worker.fail(taskWithId.getId(), Mono.just(Optional.empty()), "Publishing task failed", e))
283+
.then(Mono.error(e));
284+
})
285+
.block();
286+
LOGGER.info("Submitted task {} ({})", taskWithId.getId(), taskWithId.getTask().getClass());
278287
} catch (JsonProcessingException e) {
279288
throw new RuntimeException(e);
280289
}
281290
}
282291

283292
@Override
284293
public void cancel(TaskId taskId) {
294+
LOGGER.info("Requesting cancel for task {}", taskId);
285295
sendCancelRequestsQueue.emitNext(taskId, FAIL_FAST);
286296
}
287297

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# 75. Deletes Message Vault
2+
3+
Date: 2025-12-19
4+
5+
## Status
6+
7+
Accepted (lazy consensus) & implemented.
8+
9+
This ADR is written at posteriori in order to capture knowlege of the team following the write up
10+
of [0074-dedicated-eventbus-for-message-content-deletion.md](0074-dedicated-eventbus-for-message-content-deletion.md)
11+
12+
## Context
13+
14+
In standard email systems, when a message is deleted—whether by a user, an administrator, or automatically—it is often permanently lost. This creates several
15+
problems in environments where email is a critical business record.
16+
17+
As such we want a mechanism that protects against:
18+
19+
- **Accidental or malicious deletion**: Users can accidentally delete important emails, or malicious actors may intentionally remove messages to hide evidence. Recovery
20+
shall be possible.
21+
- **Legal and regulatory compliance**: Many organizations must comply with regulations that require retention of business communications and the ability to produce
22+
deleted emails during audits or legal discovery.
23+
- **Administrator control and traceability**: administrator needs to control and understand destructive actions done on user account (date of deletion and what had been deleted).
24+
25+
However we want a clear **separation of user experience and data retention**. Users expect that “delete” means the message disappears from their mailbox.
26+
27+
However incident recovery, and Recovery Point Objectives are a non objective that shall be addressed through global database level backups.
28+
29+
## Decision
30+
31+
Provide a James mailbox plugin, bundled in Guice application, the **Deleted Message Vault**.
32+
33+
Provide an implementation of this vault atop the object store, which stores data in a time organized fashion (per month) in a dedicated buckets. Minimal
34+
metadata are to be kept onto the Cassandra / Postgres database.
35+
36+
Provide a webadmin endpoints for restoring user data, with a minimal and simple fliter logic to allow restoring specific content. This action is not exposed to the end user.
37+
38+
Provide a webadmin endpoint to access deleted messages if need be.
39+
40+
Provide a webadmin endpoint to delete the vault data that no longer needs to be retained.
41+
42+
Plug this vault onto the mailbox deletion process. We leverage [0029-Cassandra-mailbox-deletion-cleanup.md](0029-Cassandra-mailbox-deletion-cleanup.md) asynchronous deletion listener as well as
43+
the [0074-dedicated-eventbus-for-message-content-deletion.md](0074-dedicated-eventbus-for-message-content-deletion.md) dedicated event bus for effective content deletion to do so. Please
44+
note that blob deduplication mentioned in [0049-deduplicated-blobs-gs-with-bloom-filters.md](0049-deduplicated-blobs-gs-with-bloom-filters.md) needs to be active.
45+
46+
## Consequences
47+
48+
Aforementioned objectives are attained.
49+
50+
An extra copy is needed upon deletes which can be expensive. That is why we needed [0074-dedicated-eventbus-for-message-content-deletion.md](0074-dedicated-eventbus-for-message-content-deletion.md)
51+
especially upon large mailbox deletion.
52+
53+
Disk space impact:
54+
- Deleted message vault is not counted onto the user quota
55+
- Content is not deduplicated onto the deleted message vault
56+
57+
# References
58+
59+
- [0029-Cassandra-mailbox-deletion-cleanup.md](0029-Cassandra-mailbox-deletion-cleanup.md)
60+
- [0049-deduplicated-blobs-gs-with-bloom-filters.md](0049-deduplicated-blobs-gs-with-bloom-filters.md)
61+
- [0074-dedicated-eventbus-for-message-content-deletion.md](0074-dedicated-eventbus-for-message-content-deletion.md)
62+
- [Asynchronous deletions with DeletedMessageVault on top of Cassandra](https://issues.apache.org/jira/browse/JAMES-3882)
63+

0 commit comments

Comments
 (0)