You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Refined phrasing, grammar, and formatting across multiple files to enhance readability and accessibility. Updated code examples for better clarity and alignment with best practices.
Subscriber filters are similar to ASP.NET MVC filters and are mainly used to process additional work before and after the subscriber method is executed. Such as transaction management or logging, etc.
3
+
Subscriber filters are similar to ASP.NET MVC filters and are mainly used to perform additional work before and after the subscriber method executes, such as transaction management or logging.
4
4
5
-
## Create subscribe filter
5
+
## Creating a Subscriber Filter
6
6
7
7
### Create Filter
8
8
9
-
Create a new filter class and inherit the `SubscribeFilter` abstract class.
9
+
Create a new filter class that inherits from the `SubscribeFilter` abstract class.
//Handle exceptions during subscriber method execution
27
27
}
28
28
}
29
29
```
30
30
31
-
In some scenarios, if you want to terminate the subscriber method execution, you can throw an exception in `OnSubscribeExecutingAsync`, and choose to ignore the exception in `OnSubscribeExceptionAsync`.
32
-
33
-
To ignore exceptions, you can setting `context.ExceptionHandled = true` in `ExceptionContext`
31
+
In some scenarios, if you want to terminate the subscriber method execution, you can throw an exception in `OnSubscribeExecutingAsync`, and choose to handle the exception in `OnSubscribeExceptionAsync`.
34
32
33
+
To ignore exceptions, set `context.ExceptionHandled = true` in `ExceptionContext`:
Imdempotence (which you may read a formal definition of on [Wikipedia](https://en.wikipedia.org/wiki/Idempotence), when we are talking about messaging, is when a message redelivery can be handled without ending up in an unintended state.
3
+
Idempotence (which you can read a formal definition of on [Wikipedia](https://en.wikipedia.org/wiki/Idempotence)) in messaging systems means that a message redelivery can be handled without resulting in an unintended state.
4
4
5
-
## Delivery guarantees[^1]
5
+
## Delivery Guarantees[^1]
6
6
7
7
[^1]: The chapter refers to the [Delivery guarantees](https://github.com/rebus-org/Rebus/wiki/Delivery-guarantees) of rebus, which I think is described very good.
8
8
9
-
Before we talk about idempotency, let's talk about the delivery of messages on the consumer side.
9
+
Before discussing idempotency, let's discuss message delivery guarantees on the consumer side.
10
10
11
-
Since CAP doesn't uses MS DTC or other type of 2PC distributed transaction mechanism, there is a problem that the message is strictly delivered at least once. Specifically, in a message-based system, there are three possibilities:
11
+
Since CAP doesn't use MS DTC or other 2PC (Two-Phase Commit) distributed transaction mechanisms, there is an inherent limitation: messages are delivered at least once. Specifically, in a message-based system, there are three possibilities:
12
12
13
-
* Exactly Once(*)
13
+
* Exactly Once(*)
14
14
* At Most Once
15
15
* At Least Once
16
16
17
-
Exactly once has a (*) next to it, because in the general case, it is simply not possible.
17
+
Exactly Once has a (*) next to it because, in the general case, it is simply not possible.
18
18
19
19
### At Most Once
20
20
21
-
The At Most Once delivery guarantee covers the case when you are guaranteed to receive all messages either once, or maybe not at all.
22
-
23
-
This type of delivery guarantee can arise from your messaging system and your code performing its actions in the following order:
21
+
The At Most Once delivery guarantee ensures that you receive all messages either once or not at all.
24
22
23
+
This type of delivery guarantee can arise from your messaging system and your code performing actions in the following order:
25
24
26
25
```
27
26
1. Remove message from queue
@@ -35,19 +34,19 @@ This type of delivery guarantee can arise from your messaging system and your co
35
34
2. Put message back into the queue
36
35
```
37
36
38
-
In the best case scenario, this is all well and good – your messages will be received, and work transactions will be committed, and you will be happy.
37
+
In the best case scenario, this works well – your messages will be received, work transactions will be committed, and you will be happy.
39
38
40
-
However, the sun does not always shine, and stuff tends to fail – especially if you do alot of stuff. Consider e.g. what would happen if anything fails after having performed step (1), and then – when you try to execute step (4)/(2) (i.e. put the message back into the queue) – the network was temporarily unavailable, or the message broker restarted, or the host machine decided to reboot because it had installed an update.
39
+
However, things can fail – especially if you do a lot of work. For example, consider what happens if anything fails after step (1), and then – when you try to execute step (4)/(2) (i.e., put the message back into the queue) – the network becomes temporarily unavailable, the message broker restarts, or the host machine reboots due to a system update.
41
40
42
-
This can be OK if it's what you want, but most things in CAP revolve around the concept of DURABLE messages, i.e. messages whose contents is just as important as the data in your database.
41
+
This might be acceptable if that's what you want, but most things in CAP revolve around the concept of DURABLE messages – messages whose contents are as important as the data in your database.
43
42
44
43
### At Least Once
45
44
46
-
This delivery guarantee covers the case when you are guaranteed to receive all messages either once, or maybe more times if something has failed.
45
+
The At Least Once delivery guarantee ensures that you receive all messages one or more times if something fails.
47
46
48
-
It requires a slight change to the order we are executing our steps in, and it requires that the message queue system supports transactions, either in the form of the traditional begin-commit-rollback protocol (MSMQ does this), or in the form of a receive-ack-nack protocol (RabbitMQ, Azure Service Bus, etc. do this).
47
+
This requires a slight change in the order of execution and requires that the message queue system supports transactions, either through the traditional begin-commit-rollback protocol (MSMQ does this) or through a receive-ack-nack protocol (RabbitMQ, Azure Service Bus, etc. do this).
49
48
50
-
Check this out – if we do this:
49
+
Consider this approach:
51
50
52
51
```
53
52
1. Grab lease on message in queue
@@ -62,50 +61,49 @@ Check this out – if we do this:
62
61
2. Release lease on message
63
62
```
64
63
65
-
and the "lease" we grabbed on the message in step (1) is associated with an appropriate timeout, then we are guaranteed that no matter how wrong things go, we will only actually remove the message from the queue (i.e. execute step (4)/(2)) if we have successfully committed our "work transaction".
64
+
If the "lease" grabbed in step (1) has an appropriate timeout associated with it, then we are guaranteed that no matter how wrong things go, we will only actually remove the message from the queue (step 4/2) if we have successfully committed our "work transaction".
66
65
67
-
### What is a "work transaction"?
66
+
### What is a "Work Transaction"?
68
67
69
-
It depends on what you're doing 😄 maybe it's a transaction in a relational database (which traditionally have pretty good support in this regard), maybe it's a transaction in a document database that happens to support transaction (like RavenDB or Postgres), or maybe it's a conceptual transaction in the form of whichever work you happen to carry out as a consequence of handling a message, e.g. update a bunch of documents in MongoDB, move some files around in the file system, or mutate some obscure in-mem data structure.
68
+
It depends on what you're doing 😄 Maybe it's a transaction in a relational database (which traditionally have good support for this), maybe it's a transaction in a document database that supports transactions (like RavenDB or PostgreSQL), or maybe it's a conceptual transaction representing the work you perform as a consequence of handling a message, e.g., updating documents in MongoDB, moving files in the file system, or modifying in-memory data structures.
70
69
71
-
The fact that the "work transaction" is just a conceptual thing is what makes it impossible to support the aforementioned Exactly Once delivery guarantee – it's just not generally possible to commit or roll back a "work transaction" and a "queue transaction" (which is what we could call the protocol carried out with the message queue systems) atomically and consistently.
70
+
The fact that the "work transaction" is conceptual makes it impossible to support Exactly Once delivery – it's simply not generally possible to commit or roll back a "work transaction" and a "queue transaction" (the protocol with the message queue system) atomically and consistently.
72
71
73
-
## Idempotence at CAP
72
+
## Idempotence in CAP
74
73
75
-
In CAP, **At Least Once** delivery guarantee is used.
74
+
In CAP, the **At Least Once** delivery guarantee is used.
76
75
77
-
Since we have a temporary storage medium (database table), we may be able to do At Most Once, but in order to strictly guarantee that the message will not be lost, we do not provide related functions or configurations.
76
+
Since CAP uses a temporary storage medium (database table), At Most Once could theoretically be achieved, but to strictly guarantee that messages are not lost, we do not provide related functions or configurations.
78
77
79
-
### Why are we not providing(achieving) idempotency ?
78
+
### Why We Don't Provide (Achieve) Idempotency
80
79
81
-
1.The message was successfully written, but the execution of the Consumer method failed.
80
+
1.Message successfully written, but Consumer method execution failed.
82
81
83
-
There are a lot of reasons why the Consumer method fails. I don't know if the specific scene is blindly retrying or not retrying is an incorrect choice.
84
-
For example, if the consumer is debiting service, if the execution of the debit is successful, but fails to write the debit log, the CAP will judge that the consumer failed to execute and try again. If the client does not guarantee idempotency, the framework will retry it, which will inevitably lead to serious consequences for multiple debits.
82
+
There are many reasons why the Consumer method might fail. Without knowing the specific scenario, it's unclear whether retrying blindly or not retrying is the correct choice.
83
+
For example, if the consumer is a debit service and the debit execution succeeds but fails to write the debit log, CAP will consider the consumer failed and retry. If the client doesn't guarantee idempotency, the framework will retry, inevitably leading to serious consequences like multiple debits.
85
84
86
-
2.The execution of the Consumer method succeeded, but received the same message.
85
+
2. Consumer method execution succeeded, but the same message is received again.
87
86
88
-
This scenario is also possible. If the Consumer has been successfully executed at the beginning, but for some reason, such as the Broker recovery, same message has been received, CAP will consider this as a new message after receiving the Broker message. Message will be executed again by the Consumer. Because it is a new message, CAP cannot be idempotent at this time.
87
+
This scenario is also possible. If the Consumer has already executed successfully but for some reason (e.g., broker recovery), the same message is received again, CAP will treat it as a new message. Message will be executed again by the Consumer. Because it is a new message, CAP cannot ensure idempotency at this point.
89
88
90
-
3.The current data storage mode can not be idempotent.
89
+
3.Current data storage mode cannot guarantee idempotency.
91
90
92
-
Since the table of the CAP message is deleted after 1 hour for the successfully consumed message, if the historical message cannot be idempotent. Historically, if the broker has maintained or manually processed some messages for some reason.
91
+
Since the CAP message table for successfully consumed messages is deleted after 1 hour, historical messages cannot be verified for idempotency. If the broker has been maintained or manually processed some messages for some reason, there's no way to verify if they were already processed.
93
92
94
93
4. Industry practices.
95
94
96
-
Many event-driven frameworks require users to ensure idempotent operations, such as ENode, RocketMQ, etc...
95
+
Many event-driven frameworks require users to ensure idempotent operations, such as ENode, RocketMQ, etc.
97
96
98
-
From an implementation point of view, CAP can do some less stringent idempotence, but strict idempotent can not be guaranteed.
97
+
From an implementation perspective, CAP could provide some less stringent idempotency, but strict idempotency cannot be guaranteed.
99
98
100
-
### Naturally idempotent message processing
99
+
### Naturally Idempotent Message Processing
101
100
102
-
Generally, the best way to deal with message redeliveries is to make the processing of each message naturally idempotent.
101
+
Generally, the best way to handle message redeliveries is to make the processing of each message naturally idempotent.
103
102
104
-
Natural idempotence arises when the processing of a message consists of calling an idempotent method on a domain object, like
103
+
Natural idempotence occurs when processing a message consists of calling an idempotent method on a domain object, like:
105
104
106
105
```
107
106
obj.MarkAsDeleted();
108
-
109
107
```
110
108
111
109
or
@@ -114,13 +112,13 @@ or
114
112
obj.UpdatePeriod(message.NewPeriod);
115
113
```
116
114
117
-
You can use the `INSERT ON DUPLICATE KEY UPDATE` provided by the database to easily done.
115
+
You can use `INSERT ON DUPLICATE KEY UPDATE` provided by the database to achieve this easily.
118
116
119
-
### Explicitly handling redeliveries
117
+
### Explicitly Handling Redeliveries
120
118
121
-
Another way of making message processing idempotent, is to simply track IDs of processed messages explicitly, and then make your code handle a redelivery.
119
+
Another way to make message processing idempotent is to explicitly track IDs of processed messages and then handle redeliveries in your code.
122
120
123
-
Assuming that you are keeping track of message IDs by using an `IMessageTracker` that uses the same transactional data store as the rest of your work, your code might look somewhat like this:
121
+
Assuming you track message IDs using an `IMessageTracker` that uses the same transactional data store as the rest of your work, your code might look like this:
124
122
125
123
```c#
126
124
readonlyIMessageTracker_messageTracker;
@@ -138,12 +136,12 @@ public async Task Handle(SomeMessage message)
138
136
return;
139
137
}
140
138
141
-
//do the work here
139
+
//Do the actual work here
142
140
// ...
143
141
144
-
//remember that this message has been processed
145
-
await_messageTracker.MarkAsProcessed(messageId);
142
+
//Record that this message has been processed
143
+
await_messageTracker.MarkAsProcessed(message.Id);
146
144
}
147
145
```
148
146
149
-
As for the implementation of `IMessageTracker`, you can use a storage message Id such as Redis or a database and the corresponding processing state.
147
+
For the `IMessageTracker` implementation, you can use a message ID storage system like Redis or a database with a corresponding processing state.
0 commit comments