Skip to content

Commit 6afc45d

Browse files
committed
Improve clarity and consistency in documentation
Refined phrasing, grammar, and formatting across multiple files to enhance readability and accessibility. Updated code examples for better clarity and alignment with best practices.
1 parent f58038c commit 6afc45d

32 files changed

+391
-440
lines changed

docs/content/user-guide/en/cap/configuration.md

Lines changed: 62 additions & 62 deletions
Large diffs are not rendered by default.
Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
11
# Filter
22

3-
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.
44

5-
## Create subscribe filter
5+
## Creating a Subscriber Filter
66

77
### Create Filter
88

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.
1010

1111
```C#
12-
public class MyCapFilter: SubscribeFilter
12+
public class MyCapFilter : SubscribeFilter
1313
{
1414
public override Task OnSubscribeExecutingAsync(ExecutingContext context)
1515
{
16-
// before subscribe method exectuing
16+
// Execute before the subscriber method runs
1717
}
1818

1919
public override Task OnSubscribeExecutedAsync(ExecutedContext context)
2020
{
21-
// after subscribe method executed
21+
// Execute after the subscriber method completes
2222
}
2323

2424
public override Task OnSubscribeExceptionAsync(ExceptionContext context)
2525
{
26-
// subscribe method execution exception
26+
// Handle exceptions during subscriber method execution
2727
}
2828
}
2929
```
3030

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`.
3432

33+
To ignore exceptions, set `context.ExceptionHandled = true` in `ExceptionContext`:
3534

3635
```C#
3736
public override Task OnSubscribeExceptionAsync(ExceptionContext context)
@@ -40,15 +39,15 @@ public override Task OnSubscribeExceptionAsync(ExceptionContext context)
4039
}
4140
```
4241

43-
### Configuration Filter
42+
### Registering a Filter
4443

45-
Use `AddSubscribeFilter<>` to add a filter.
44+
Use `AddSubscribeFilter<>` to register a filter.
4645

4746
```C#
4847
services.AddCap(opt =>
4948
{
50-
// ***
51-
}.AddSubscribeFilter<MyCapFilter>();
49+
// ...
50+
}).AddSubscribeFilter<MyCapFilter>();
5251
```
5352

54-
Currently, we do not support adding multiple filters.
53+
Currently, multiple filters are not supported.
Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
11
# Idempotence
22

3-
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.
44

5-
## Delivery guarantees[^1]
5+
## Delivery Guarantees[^1]
66

77
[^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.
88

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.
1010

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:
1212

13-
* Exactly Once(*)
13+
* Exactly Once (*)
1414
* At Most Once
1515
* At Least Once
1616

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.
1818

1919
### At Most Once
2020

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.
2422

23+
This type of delivery guarantee can arise from your messaging system and your code performing actions in the following order:
2524

2625
```
2726
1. Remove message from queue
@@ -35,19 +34,19 @@ This type of delivery guarantee can arise from your messaging system and your co
3534
2. Put message back into the queue
3635
```
3736

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.
3938

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.
4140

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.
4342

4443
### At Least Once
4544

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.
4746

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).
4948

50-
Check this out – if we do this:
49+
Consider this approach:
5150

5251
```
5352
1. Grab lease on message in queue
@@ -62,50 +61,49 @@ Check this out – if we do this:
6261
2. Release lease on message
6362
```
6463

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".
6665

67-
### What is a "work transaction"?
66+
### What is a "Work Transaction"?
6867

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.
7069

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.
7271

73-
## Idempotence at CAP
72+
## Idempotence in CAP
7473

75-
In CAP, **At Least Once** delivery guarantee is used.
74+
In CAP, the **At Least Once** delivery guarantee is used.
7675

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.
7877

79-
### Why are we not providing(achieving) idempotency ?
78+
### Why We Don't Provide (Achieve) Idempotency
8079

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.
8281

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.
8584

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.
8786

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.
8988

90-
3. The current data storage mode can not be idempotent.
89+
3. Current data storage mode cannot guarantee idempotency.
9190

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.
9392

9493
4. Industry practices.
9594

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.
9796

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.
9998

100-
### Naturally idempotent message processing
99+
### Naturally Idempotent Message Processing
101100

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.
103102

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:
105104

106105
```
107106
obj.MarkAsDeleted();
108-
109107
```
110108

111109
or
@@ -114,13 +112,13 @@ or
114112
obj.UpdatePeriod(message.NewPeriod);
115113
```
116114

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.
118116

119-
### Explicitly handling redeliveries
117+
### Explicitly Handling Redeliveries
120118

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.
122120

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:
124122

125123
```c#
126124
readonly IMessageTracker _messageTracker;
@@ -138,12 +136,12 @@ public async Task Handle(SomeMessage message)
138136
return;
139137
}
140138

141-
// do the work here
139+
// Do the actual work here
142140
// ...
143141
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);
146144
}
147145
```
148146

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

Comments
 (0)