Commit ba6a81c
Change feed: Adds id and pk to ChangeFeedMetadata for delete operations (#5470)
## Description
Adds id and partition key to ChangeFeedMetadata. This is populated for
delete operations using all versions all deletes change feed mode so
that when customers get the delete, they know what was deleted. Note it
doesn't have the value of the document that was deleted only the id and
partition key through which a document can be uniquely identified.
Please note that user provided serializer is not being used.
This PR includes commits from #5191 and additional simplifications and
unit test changes
## Type of change
New feature (non-breaking change which adds functionality)
## Basic Usage
### Single Partition Key Example
```csharp
using Microsoft.Azure.Cosmos;
public class Product
{
public string id { get; set; }
public string categoryId { get; set; }
public string name { get; set; }
public decimal price { get; set; }
}
// Create Change Feed Processor with All Versions and Deletes mode
ChangeFeedProcessor processor = container
.GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes<Product>(
processorName: "productProcessor",
onChangesDelegate: async (context, changes, cancellationToken) =>
{
foreach (ChangeFeedItem<Product> change in changes)
{
switch (change.Metadata.OperationType)
{
case ChangeFeedOperationType.Create:
case ChangeFeedOperationType.Replace:
Console.WriteLine($"Document: {change.Current.id}");
break;
case ChangeFeedOperationType.Delete:
// Access deleted document's id and partition key from metadata
Console.WriteLine($"Deleted Document Id: {change.Metadata.Id}");
// PartitionKey is a dictionary: key = path name, value = partition key value
string categoryId = change.Metadata.PartitionKey["categoryId"]?.ToString();
Console.WriteLine($"Partition Key: {categoryId}");
// Check if deleted due to TTL expiration
if (change.Metadata.IsTimeToLiveExpired)
{
Console.WriteLine("Deleted via TTL expiration");
}
// Cleanup related data
await RemoveFromCacheAsync(change.Metadata.Id, categoryId);
break;
}
}
})
.WithInstanceName("instance1")
.WithLeaseContainer(leaseContainer)
.Build();
await processor.StartAsync();
```
### Hierarchical Partition Key Example
```csharp
public class UserSession
{
public string id { get; set; }
public string tenantId { get; set; }
public string userId { get; set; }
public string sessionId { get; set; }
}
// Container with HPK: ["/tenantId", "/userId", "/sessionId"]
ChangeFeedProcessor processor = container
.GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes<UserSession>(
processorName: "sessionProcessor",
onChangesDelegate: async (context, changes, cancellationToken) =>
{
foreach (ChangeFeedItem<UserSession> change in changes)
{
if (change.Metadata.OperationType == ChangeFeedOperationType.Delete)
{
Console.WriteLine($"Deleted Session Id: {change.Metadata.Id}");
// PartitionKey dictionary contains all hierarchy levels in order
string tenantId = change.Metadata.PartitionKey["tenantId"]?.ToString();
string userId = change.Metadata.PartitionKey["userId"]?.ToString();
string sessionId = change.Metadata.PartitionKey["sessionId"]?.ToString();
Console.WriteLine($"HPK: tenant={tenantId}, user={userId}, session={sessionId}");
await CleanupSessionAsync(tenantId, userId, sessionId);
}
}
})
.WithInstanceName("instance1")
.WithLeaseContainer(leaseContainer)
.Build();
await processor.StartAsync();
```
### Cache Synchronization Example
```csharp
ChangeFeedProcessor processor = container
.GetChangeFeedProcessorBuilderWithAllVersionsAndDeletes<dynamic>(
processorName: "cacheSync",
onChangesDelegate: async (context, changes, cancellationToken) =>
{
foreach (ChangeFeedItem<dynamic> change in changes)
{
if (change.Metadata.OperationType == ChangeFeedOperationType.Delete)
{
// Get id and partition key from metadata
string id = change.Metadata.Id;
string pk = change.Metadata.PartitionKey.Values.FirstOrDefault()?.ToString();
// Remove from cache and search index
await cache.RemoveAsync(id);
await searchIndex.DeleteAsync(id);
Console.WriteLine($"Removed {id} from cache and index");
}
else
{
// Update cache and index for creates/updates
await cache.SetAsync(change.Current.id.ToString(), change.Current);
await searchIndex.IndexAsync(change.Current);
}
}
})
.WithInstanceName("cacheSync1")
.WithLeaseContainer(leaseContainer)
.Build();
await processor.StartAsync();
```
## Property Details
### `ChangeFeedMetadata.Id`
- **Type**: `string`
- **Populated**: Delete operations only (null for create/replace)
- **Description**: The document id of the deleted item
- **Example**: `"order-12345"`
### `ChangeFeedMetadata.PartitionKey`
- **Type**: `Dictionary<string, object>`
- **Populated**: Delete operations only (null for create/replace)
- **Description**: Dictionary with partition key path(s) as keys and
values as objects
- **Key Format**: Partition key property name without leading `/`
- **Value Types**: Can be string, number, boolean, or null
**Examples:**
Single Partition Key (`/categoryId`):
```csharp
{
"categoryId": "electronics"
}
```
Hierarchical Partition Key (`["/tenantId", "/userId", "/sessionId"]`):
```csharp
{
"tenantId": "tenant123",
"userId": "user456",
"sessionId": "session789"
}
```
Mixed Types (`["/category", "/priority", "/isActive"]`):
```csharp
{
"category": "electronics",
"priority": 1,
"isActive": true
}
```
## Notes
1. **Only for Deletes**: `Id` and `PartitionKey` are only populated when
`OperationType == Delete`
2. **No Document Body**: The deleted document body is not available
(Current will be null)
3. **No Custom Serializer**: User-provided serializers are not applied
to these metadata properties
4. **TTL Detection**: Use `IsTimeToLiveExpired` to distinguish TTL
deletes from explicit deletes
5. **Container Setup Required**: Container must have
`ChangeFeedPolicy.FullFidelityRetention` configured
---------
Co-authored-by: Justine Cocchi <jucocchi@microsoft.com>
Co-authored-by: Dikshi Bahl <DikshiBahl@microsoft.com>
Co-authored-by: dibahlfi <106994927+dibahlfi@users.noreply.github.com>
Co-authored-by: Kiran Kumar Kolli <kirankk@microsoft.com>
Co-authored-by: ananth7592 <amudumba@microsoft.com>1 parent 0be9384 commit ba6a81c
10 files changed
Lines changed: 1082 additions & 740 deletions
File tree
- Microsoft.Azure.Cosmos
- src/Resource/FullFidelity
- Converters
- tests
- Microsoft.Azure.Cosmos.EmulatorTests
- CFP/AllVersionsAndDeletes
- FeedToken
- Microsoft.Azure.Cosmos.Tests/Contracts
Lines changed: 4 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
| 61 | + | |
61 | 62 | | |
62 | 63 | | |
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
66 | 67 | | |
67 | | - | |
| 68 | + | |
68 | 69 | | |
69 | 70 | | |
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
74 | | - | |
| 75 | + | |
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
| |||
Lines changed: 80 additions & 9 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | | - | |
11 | 10 | | |
12 | 11 | | |
13 | 12 | | |
14 | 13 | | |
15 | 14 | | |
16 | 15 | | |
17 | 16 | | |
18 | | - | |
19 | 17 | | |
20 | 18 | | |
21 | 19 | | |
22 | 20 | | |
23 | 21 | | |
24 | | - | |
| 22 | + | |
25 | 23 | | |
| 24 | + | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
30 | | - | |
31 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
32 | 37 | | |
33 | 38 | | |
34 | 39 | | |
35 | 40 | | |
| 41 | + | |
| 42 | + | |
36 | 43 | | |
37 | 44 | | |
38 | 45 | | |
39 | 46 | | |
40 | 47 | | |
41 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
42 | 53 | | |
43 | | - | |
44 | 54 | | |
45 | 55 | | |
46 | 56 | | |
47 | 57 | | |
48 | 58 | | |
| 59 | + | |
| 60 | + | |
49 | 61 | | |
50 | 62 | | |
51 | 63 | | |
52 | 64 | | |
53 | | - | |
| 65 | + | |
54 | 66 | | |
| 67 | + | |
| 68 | + | |
55 | 69 | | |
56 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
57 | 128 | | |
58 | 129 | | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
15 | 17 | | |
Lines changed: 0 additions & 92 deletions
This file was deleted.
Lines changed: 13 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
72 | | - | |
| 72 | + | |
| 73 | + | |
73 | 74 | | |
74 | 75 | | |
75 | 76 | | |
| |||
84 | 85 | | |
85 | 86 | | |
86 | 87 | | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
148 | | - | |
| 148 | + | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
| 158 | + | |
| 159 | + | |
158 | 160 | | |
159 | 161 | | |
160 | 162 | | |
| |||
171 | 173 | | |
172 | 174 | | |
173 | 175 | | |
174 | | - | |
175 | | - | |
176 | | - | |
| 176 | + | |
| 177 | + | |
177 | 178 | | |
178 | 179 | | |
179 | 180 | | |
180 | 181 | | |
181 | | - | |
| 182 | + | |
182 | 183 | | |
183 | 184 | | |
184 | 185 | | |
| |||
211 | 212 | | |
212 | 213 | | |
213 | 214 | | |
| 215 | + | |
| 216 | + | |
214 | 217 | | |
215 | | - | |
216 | 218 | | |
217 | 219 | | |
218 | 220 | | |
| |||
0 commit comments