@@ -15,10 +15,12 @@ namespace KustoCopyConsole.Storage
15
15
internal class RowItemGateway : IAsyncDisposable
16
16
{
17
17
private static readonly Version CURRENT_FILE_VERSION = new Version ( 0 , 0 , 1 , 0 ) ;
18
+ private static readonly TimeSpan MAX_BUFFER_TIME = TimeSpan . FromSeconds ( 5 ) ;
18
19
19
20
private readonly IAppendStorage _appendStorage ;
20
21
private readonly Func < IEnumerable < RowItem > , IEnumerable < RowItem > > _compactFunc ;
21
- private readonly MemoryStream _memoryStream = new MemoryStream ( ) ;
22
+ private readonly MemoryStream _bufferStream = new MemoryStream ( ) ;
23
+ private DateTime ? _bufferStartTime ;
22
24
23
25
public RowItemGateway (
24
26
IAppendStorage appendStorage ,
@@ -30,7 +32,7 @@ public RowItemGateway(
30
32
31
33
async ValueTask IAsyncDisposable . DisposeAsync ( )
32
34
{
33
- await FlushAsync ( ) ;
35
+ await FlushAsync ( CancellationToken . None ) ;
34
36
await _appendStorage . DisposeAsync ( ) ;
35
37
}
36
38
@@ -39,24 +41,35 @@ public async Task<IImmutableList<RowItem>> MigrateToLatestVersionAsync(Cancellat
39
41
return await CompactAsync ( ct ) ;
40
42
}
41
43
42
- public Task AppendAsync ( RowItem item , CancellationToken ct )
44
+ public async Task AppendAsync ( RowItem item , CancellationToken ct )
43
45
{
44
- item . Validate ( ) ;
45
- throw new NotImplementedException ( ) ;
46
- }
47
-
48
- public Task AppendAtomicallyAsync ( IEnumerable < RowItem > items , CancellationToken ct )
49
- {
50
- //item.Validate();
46
+ var bufferToWrite = AppendToBuffer ( item ) ;
51
47
52
- throw new NotImplementedException ( ) ;
48
+ if ( bufferToWrite == null
49
+ && _bufferStartTime != null
50
+ && DateTime . Now - _bufferStartTime > MAX_BUFFER_TIME )
51
+ {
52
+ await FlushAsync ( ct ) ;
53
+ bufferToWrite = _bufferStream . ToArray ( ) ;
54
+ _bufferStream . SetLength ( 0 ) ;
55
+ _bufferStartTime = null ;
56
+ }
57
+ if ( bufferToWrite != null )
58
+ {
59
+ await _appendStorage . AtomicAppendAsync ( bufferToWrite , ct ) ;
60
+ }
53
61
}
54
62
55
- public async Task FlushAsync ( )
63
+ public async Task FlushAsync ( CancellationToken ct )
56
64
{
57
- await Task . CompletedTask ;
65
+ var bufferToWrite = _bufferStream . ToArray ( ) ;
66
+
67
+ _bufferStream . SetLength ( 0 ) ;
68
+ _bufferStartTime = null ;
69
+ await _appendStorage . AtomicAppendAsync ( bufferToWrite , ct ) ;
58
70
}
59
71
72
+ #region Compaction
60
73
private async Task < IImmutableList < RowItem > > CompactAsync ( CancellationToken ct )
61
74
{
62
75
var readBuffer = await _appendStorage . LoadAllAsync ( ct ) ;
@@ -103,14 +116,13 @@ private async Task<IImmutableList<RowItem>> CompactAsync(CancellationToken ct)
103
116
csv . WriteHeader < RowItem > ( ) ;
104
117
csv . NextRecord ( ) ;
105
118
csv . WriteRecords ( items ) ;
106
- csv . NextRecord ( ) ;
107
119
csv . Flush ( ) ;
108
120
writer . Flush ( ) ;
109
121
110
122
var writeBuffer = tempMemoryStream . ToArray ( ) ;
111
123
112
124
await _appendStorage . AtomicReplaceAsync ( writeBuffer , ct ) ;
113
-
125
+
114
126
return items ;
115
127
}
116
128
}
@@ -146,5 +158,58 @@ private IImmutableList<RowItem> CompactBuffer(byte[] readBuffer)
146
158
return allNewItems . ToImmutableArray ( ) ;
147
159
}
148
160
}
161
+ #endregion
162
+
163
+ private byte [ ] ? AppendToBuffer ( RowItem item )
164
+ {
165
+ item . Validate ( ) ;
166
+ lock ( _bufferStream )
167
+ {
168
+ var lengthBefore = _bufferStream . Length ;
169
+
170
+ using ( var writer = new StreamWriter ( _bufferStream , leaveOpen : true ) )
171
+ using ( var csv = new CsvWriter ( writer , CultureInfo . InvariantCulture ) )
172
+ {
173
+ csv . WriteRecord ( item ) ;
174
+ csv . NextRecord ( ) ;
175
+ csv . Flush ( ) ;
176
+ writer . Flush ( ) ;
177
+ }
178
+
179
+ var lengthAfter = _bufferStream . Length ;
180
+
181
+ if ( lengthAfter > _appendStorage . MaxBufferSize )
182
+ { // Buffer is too long: write buffer before this item
183
+ if ( lengthBefore == 0 )
184
+ {
185
+ throw new CopyException (
186
+ $ "Buffer to write to the log is too long: { lengthAfter } ",
187
+ false ) ;
188
+ }
189
+ _bufferStream . SetLength ( lengthBefore ) ;
190
+
191
+ var allBuffer = _bufferStream . ToArray ( ) ;
192
+ var beforeBuffer = new byte [ lengthBefore ] ;
193
+ var remainBuffer = new byte [ lengthAfter - lengthBefore ] ;
194
+
195
+ Array . Copy ( allBuffer , beforeBuffer , lengthBefore ) ;
196
+ Array . Copy ( allBuffer , lengthBefore , remainBuffer , 0 , remainBuffer . Length ) ;
197
+ _bufferStream . SetLength ( 0 ) ;
198
+ _bufferStream . Write (
199
+ allBuffer ,
200
+ ( int ) lengthBefore ,
201
+ ( int ) ( lengthAfter - lengthBefore ) ) ;
202
+ _bufferStartTime = DateTime . Now ;
203
+
204
+ return beforeBuffer ;
205
+ }
206
+ else if ( _bufferStartTime == null )
207
+ {
208
+ _bufferStartTime = DateTime . Now ;
209
+ }
210
+ }
211
+
212
+ return null ;
213
+ }
149
214
}
150
215
}
0 commit comments