1
- import { api , auth , ConfigurationFileSyncRulesProvider , replication , system } from '@powersync/service-core' ;
1
+ import {
2
+ api ,
3
+ auth ,
4
+ ConfigurationFileSyncRulesProvider ,
5
+ replication ,
6
+ system ,
7
+ TearDownOptions
8
+ } from '@powersync/service-core' ;
2
9
import * as jpgwire from '@powersync/service-jpgwire' ;
3
10
import * as types from '../types/types.js' ;
4
11
import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js' ;
5
12
import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js' ;
6
13
import { WalStreamReplicator } from '../replication/WalStreamReplicator.js' ;
7
14
import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js' ;
8
15
import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js' ;
16
+ import { cleanUpReplicationSlot } from '../replication/replication-utils.js' ;
17
+ import { PgManager } from '../replication/PgManager.js' ;
9
18
10
19
export class PostgresModule extends replication . ReplicationModule < types . PostgresConnectionConfig > {
11
20
constructor ( ) {
@@ -24,29 +33,28 @@ export class PostgresModule extends replication.ReplicationModule<types.Postgres
24
33
this . registerSupabaseAuth ( context ) ;
25
34
}
26
35
27
- jpgwire . setMetricsRecorder ( {
28
- addBytesRead ( bytes ) {
29
- context . metrics . data_replicated_bytes . add ( bytes ) ;
30
- }
31
- } ) ;
36
+ if ( context . metrics ) {
37
+ jpgwire . setMetricsRecorder ( {
38
+ addBytesRead ( bytes ) {
39
+ context . metrics ! . data_replicated_bytes . add ( bytes ) ;
40
+ }
41
+ } ) ;
42
+ }
32
43
}
33
44
34
- protected createRouteAPIAdapter ( decodedConfig : types . PostgresConnectionConfig ) : api . RouteAPI {
35
- return new PostgresRouteAPIAdapter ( this . resolveConfig ( decodedConfig ) ) ;
45
+ protected createRouteAPIAdapter ( ) : api . RouteAPI {
46
+ return new PostgresRouteAPIAdapter ( this . resolveConfig ( this . decodedConfig ! ) ) ;
36
47
}
37
48
38
- protected createReplicator (
39
- decodedConfig : types . PostgresConnectionConfig ,
40
- context : system . ServiceContext
41
- ) : replication . AbstractReplicator {
42
- const normalisedConfig = this . resolveConfig ( decodedConfig ) ;
43
- const connectionFactory = new ConnectionManagerFactory ( normalisedConfig ) ;
49
+ protected createReplicator ( context : system . ServiceContext ) : replication . AbstractReplicator {
50
+ const normalisedConfig = this . resolveConfig ( this . decodedConfig ! ) ;
44
51
const syncRuleProvider = new ConfigurationFileSyncRulesProvider ( context . configuration . sync_rules ) ;
52
+ const connectionFactory = new ConnectionManagerFactory ( normalisedConfig ) ;
45
53
46
54
return new WalStreamReplicator ( {
47
55
id : this . getDefaultId ( normalisedConfig . database ) ,
48
56
syncRuleProvider : syncRuleProvider ,
49
- storageEngine : context . storage ,
57
+ storageEngine : context . storageEngine ,
50
58
connectionFactory : connectionFactory ,
51
59
rateLimiter : new PostgresErrorRateLimiter ( )
52
60
} ) ;
@@ -62,7 +70,29 @@ export class PostgresModule extends replication.ReplicationModule<types.Postgres
62
70
} ;
63
71
}
64
72
65
- async teardown ( ) : Promise < void > { }
73
+ async teardown ( options : TearDownOptions ) : Promise < void > {
74
+ const normalisedConfig = this . resolveConfig ( this . decodedConfig ! ) ;
75
+ const connectionManager = new PgManager ( normalisedConfig , {
76
+ idleTimeout : 30_000 ,
77
+ maxSize : 1
78
+ } ) ;
79
+
80
+ try {
81
+ if ( options . syncRules ) {
82
+ // TODO: In the future, once we have more replication types, we will need to check if these syncRules are for Postgres
83
+ for ( let syncRules of options . syncRules ) {
84
+ try {
85
+ await cleanUpReplicationSlot ( syncRules . slot_name , connectionManager . pool ) ;
86
+ } catch ( e ) {
87
+ // Not really much we can do here for failures, most likely the database is no longer accessible
88
+ this . logger . warn ( `Failed to fully clean up Postgres replication slot: ${ syncRules . slot_name } ` , e ) ;
89
+ }
90
+ }
91
+ }
92
+ } finally {
93
+ await connectionManager . end ( ) ;
94
+ }
95
+ }
66
96
67
97
// TODO: This should rather be done by registering the key collector in some kind of auth engine
68
98
private registerSupabaseAuth ( context : system . ServiceContextContainer ) {
0 commit comments