11using LexCore . Utils ;
22using LexData ;
33using LinqToDB ;
4+ using LinqToDB . Data ;
45using LinqToDB . EntityFrameworkCore ;
56using LinqToDB . EntityFrameworkCore . Internal ;
67using SIL . Harmony . Core ;
@@ -9,53 +10,34 @@ namespace LexBoxApi.Services;
910
1011public class CrdtCommitService ( LexBoxDbContext dbContext )
1112{
12- public async Task AddCommits ( Guid projectId , IAsyncEnumerable < ServerCommit > commits )
13+ public async Task AddCommits ( Guid projectId , IAsyncEnumerable < ServerCommit > commits , CancellationToken token = default )
1314 {
14- await using var transaction = await dbContext . Database . BeginTransactionAsync ( ) ;
15- var commitsTable = dbContext . CreateLinqToDBContext ( ) . GetTable < ServerCommit > ( ) ;
16- const int commitsThreshold = 100 ;
17- const int changeThreshold = 150 ;
18- var currentChangeCount = 0 ;
19- var commitBucket = new List < ServerCommit > ( 100 ) ;
20- await foreach ( var serverCommit in commits )
21- {
22- commitBucket . Add ( serverCommit ) ;
23- currentChangeCount += serverCommit . ChangeEntities . Count ;
24- if ( currentChangeCount < changeThreshold && commitBucket . Count < commitsThreshold )
15+ await using var transaction = await dbContext . Database . BeginTransactionAsync ( token ) ;
16+ var linqToDbContext = dbContext . CreateLinqToDBContext ( ) ;
17+ await using var tmpTable = await linqToDbContext . CreateTempTableAsync < ServerCommit > ( $ "tmp_crdt_commit_import_ { projectId } __ { Guid . NewGuid ( ) } " , cancellationToken : token ) ;
18+ await tmpTable . BulkCopyAsync ( new BulkCopyOptions { BulkCopyType = BulkCopyType . ProviderSpecific , MaxBatchSize = 10 } , commits , token ) ;
19+
20+ var commitsTable = linqToDbContext . GetTable < ServerCommit > ( ) ;
21+ await commitsTable
22+ . Merge ( )
23+ . Using ( tmpTable )
24+ . OnTargetKey ( )
25+ . InsertWhenNotMatched ( commit => new ServerCommit ( commit . Id )
2526 {
26- continue ;
27- }
28-
29- await FlushCommits ( ) ;
30- }
31-
32- if ( commitBucket . Count > 0 ) await FlushCommits ( ) ;
33-
34- await transaction . CommitAsync ( ) ;
35-
36- async Task FlushCommits ( )
37- {
38- await commitsTable
39- . Merge ( )
40- . Using ( commitBucket )
41- . OnTargetKey ( )
42- . InsertWhenNotMatched ( commit => new ServerCommit ( commit . Id )
27+ Id = commit . Id ,
28+ ClientId = commit . ClientId ,
29+ HybridDateTime = new HybridDateTime ( commit . HybridDateTime . DateTime , commit . HybridDateTime . Counter )
4330 {
44- Id = commit . Id ,
45- ClientId = commit . ClientId ,
46- HybridDateTime = new HybridDateTime ( commit . HybridDateTime . DateTime , commit . HybridDateTime . Counter )
47- {
48- DateTime = commit . HybridDateTime . DateTime , Counter = commit . HybridDateTime . Counter
49- } ,
50- ProjectId = projectId ,
51- Metadata = commit . Metadata ,
52- //without this sql cast the value will be treated as text and fail to insert into the jsonb column
53- ChangeEntities = Sql . Expr < List < ChangeEntity < ServerJsonChange > > > ( $ "{ commit . ChangeEntities } ::jsonb")
54- } )
55- . MergeAsync ( ) ;
56- commitBucket . Clear ( ) ;
57- currentChangeCount = 0 ;
58- }
31+ DateTime = commit . HybridDateTime . DateTime , Counter = commit . HybridDateTime . Counter
32+ } ,
33+ ProjectId = projectId ,
34+ Metadata = commit . Metadata ,
35+ //without this sql cast the value will be treated as text and fail to insert into the jsonb column
36+ ChangeEntities = Sql . Expr < List < ChangeEntity < ServerJsonChange > > > ( $ "{ commit . ChangeEntities } ::jsonb")
37+ } )
38+ . MergeAsync ( token ) ;
39+
40+ await transaction . CommitAsync ( token ) ;
5941 }
6042
6143 public IAsyncEnumerable < ServerCommit > GetMissingCommits ( Guid projectId , SyncState localState , SyncState remoteState )
0 commit comments