6
6
7
7
import { injectable , inject , postConstruct } from "inversify" ;
8
8
import { log } from "@gitpod/gitpod-protocol/lib/util/logging" ;
9
- import { WorkspaceDeletionService } from "../workspace/workspace-deletion-service" ;
10
9
import * as opentracing from "opentracing" ;
11
- import { TracedWorkspaceDB , DBWithTracing , WorkspaceDB } from "@gitpod/gitpod-db/lib" ;
10
+ import {
11
+ TracedWorkspaceDB ,
12
+ DBWithTracing ,
13
+ WorkspaceDB ,
14
+ WorkspaceAndOwner ,
15
+ WorkspaceOwnerAndSoftDeleted ,
16
+ } from "@gitpod/gitpod-db/lib" ;
12
17
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing" ;
13
18
import { Config } from "../config" ;
14
19
import { Job } from "./runner" ;
15
20
import { WorkspaceService } from "../workspace/workspace-service" ;
16
21
import { SYSTEM_USER } from "../authorization/authorizer" ;
22
+ import { StorageClient } from "../storage/storage-client" ;
17
23
18
24
/**
19
25
* The WorkspaceGarbageCollector has two tasks:
@@ -22,10 +28,12 @@ import { SYSTEM_USER } from "../authorization/authorizer";
22
28
*/
23
29
@injectable ( )
24
30
export class WorkspaceGarbageCollector implements Job {
25
- @inject ( WorkspaceService ) protected readonly workspaceService : WorkspaceService ;
26
- @inject ( WorkspaceDeletionService ) protected readonly deletionService : WorkspaceDeletionService ;
27
- @inject ( TracedWorkspaceDB ) protected readonly workspaceDB : DBWithTracing < WorkspaceDB > ;
28
- @inject ( Config ) protected readonly config : Config ;
31
+ constructor (
32
+ @inject ( WorkspaceService ) protected readonly workspaceService : WorkspaceService ,
33
+ @inject ( StorageClient ) protected readonly storageClient : StorageClient ,
34
+ @inject ( TracedWorkspaceDB ) protected readonly workspaceDB : DBWithTracing < WorkspaceDB > ,
35
+ @inject ( Config ) protected readonly config : Config ,
36
+ ) { }
29
37
30
38
public name = "workspace-gc" ;
31
39
public frequencyMs : number ;
@@ -56,7 +64,7 @@ export class WorkspaceGarbageCollector implements Job {
56
64
/**
57
65
* Marks old, unused workspaces as softDeleted
58
66
*/
59
- protected async softDeleteOldWorkspaces ( ) {
67
+ private async softDeleteOldWorkspaces ( ) {
60
68
if ( Date . now ( ) < this . config . workspaceGarbageCollection . startDate ) {
61
69
log . info ( "workspace-gc: garbage collection not yet active." ) ;
62
70
return ;
@@ -90,7 +98,7 @@ export class WorkspaceGarbageCollector implements Job {
90
98
}
91
99
}
92
100
93
- protected async deleteWorkspaceContentAfterRetentionPeriod ( ) {
101
+ private async deleteWorkspaceContentAfterRetentionPeriod ( ) {
94
102
const span = opentracing . globalTracer ( ) . startSpan ( "deleteWorkspaceContentAfterRetentionPeriod" ) ;
95
103
try {
96
104
const workspaces = await this . workspaceDB
@@ -99,9 +107,7 @@ export class WorkspaceGarbageCollector implements Job {
99
107
this . config . workspaceGarbageCollection . contentRetentionPeriodDays ,
100
108
this . config . workspaceGarbageCollection . contentChunkLimit ,
101
109
) ;
102
- const deletes = await Promise . all (
103
- workspaces . map ( ( ws ) => this . deletionService . garbageCollectWorkspace ( { span } , ws ) ) ,
104
- ) ;
110
+ const deletes = await Promise . all ( workspaces . map ( ( ws ) => this . garbageCollectWorkspace ( { span } , ws ) ) ) ;
105
111
106
112
log . info ( `workspace-gc: successfully deleted the content of ${ deletes . length } workspaces` ) ;
107
113
span . addTags ( { nrOfCollectedWorkspaces : deletes . length } ) ;
@@ -116,7 +122,7 @@ export class WorkspaceGarbageCollector implements Job {
116
122
/**
117
123
* This method is meant to purge all traces of a Workspace and it's WorkspaceInstances from the DB
118
124
*/
119
- protected async purgeWorkspacesAfterPurgeRetentionPeriod ( ) {
125
+ private async purgeWorkspacesAfterPurgeRetentionPeriod ( ) {
120
126
const span = opentracing . globalTracer ( ) . startSpan ( "purgeWorkspacesAfterPurgeRetentionPeriod" ) ;
121
127
try {
122
128
const now = new Date ( ) ;
@@ -151,7 +157,7 @@ export class WorkspaceGarbageCollector implements Job {
151
157
}
152
158
}
153
159
154
- protected async deleteOldPrebuilds ( ) {
160
+ private async deleteOldPrebuilds ( ) {
155
161
const span = opentracing . globalTracer ( ) . startSpan ( "deleteOldPrebuilds" ) ;
156
162
try {
157
163
const workspaces = await this . workspaceDB
@@ -160,9 +166,7 @@ export class WorkspaceGarbageCollector implements Job {
160
166
this . config . workspaceGarbageCollection . minAgePrebuildDays ,
161
167
this . config . workspaceGarbageCollection . chunkLimit ,
162
168
) ;
163
- const deletes = await Promise . all (
164
- workspaces . map ( ( ws ) => this . deletionService . garbageCollectPrebuild ( { span } , ws ) ) ,
165
- ) ;
169
+ const deletes = await Promise . all ( workspaces . map ( ( ws ) => this . garbageCollectPrebuild ( { span } , ws ) ) ) ;
166
170
167
171
log . info ( `workspace-gc: successfully deleted ${ deletes . length } prebuilds` ) ;
168
172
span . addTags ( { nrOfCollectedPrebuilds : deletes . length } ) ;
@@ -173,4 +177,75 @@ export class WorkspaceGarbageCollector implements Job {
173
177
span . finish ( ) ;
174
178
}
175
179
}
180
+
181
+ /**
182
+ * This method garbageCollects a workspace. It deletes its contents and sets the workspaces 'contentDeletedTime'
183
+ * @param ctx
184
+ * @param ws
185
+ */
186
+ private async garbageCollectWorkspace ( ctx : TraceContext , ws : WorkspaceOwnerAndSoftDeleted ) : Promise < boolean > {
187
+ const span = TraceContext . startSpan ( "garbageCollectWorkspace" , ctx ) ;
188
+
189
+ try {
190
+ const successfulDeleted = await this . deleteWorkspaceStorage ( { span } , ws , true ) ;
191
+ await this . workspaceDB
192
+ . trace ( { span } )
193
+ . updatePartial ( ws . id , { contentDeletedTime : new Date ( ) . toISOString ( ) } ) ;
194
+ return successfulDeleted ;
195
+ } catch ( err ) {
196
+ TraceContext . setError ( { span } , err ) ;
197
+ throw err ;
198
+ } finally {
199
+ span . finish ( ) ;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * @param ctx
205
+ * @param wsAndOwner
206
+ */
207
+ private async garbageCollectPrebuild ( ctx : TraceContext , ws : WorkspaceAndOwner ) : Promise < boolean > {
208
+ const span = TraceContext . startSpan ( "garbageCollectPrebuild" , ctx ) ;
209
+
210
+ try {
211
+ const successfulDeleted = await this . deleteWorkspaceStorage ( { span } , ws , true ) ;
212
+ const now = new Date ( ) . toISOString ( ) ;
213
+ // Note: soft & content deletion happens at the same time, because prebuilds are reproducible so there's no need for the extra time span.
214
+ await this . workspaceDB . trace ( { span } ) . updatePartial ( ws . id , {
215
+ contentDeletedTime : now ,
216
+ softDeletedTime : now ,
217
+ softDeleted : "gc" ,
218
+ } ) ;
219
+ return successfulDeleted ;
220
+ } catch ( err ) {
221
+ TraceContext . setError ( { span } , err ) ;
222
+ throw err ;
223
+ } finally {
224
+ span . finish ( ) ;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Performs the actual deletion of a workspace's backups (and optionally, snapshots). It:
230
+ * - throws an error if something went wrong during deletion
231
+ * - returns true in case of successful deletion
232
+ * @param ws
233
+ * @param includeSnapshots
234
+ */
235
+ private async deleteWorkspaceStorage (
236
+ ctx : TraceContext ,
237
+ ws : WorkspaceAndOwner ,
238
+ includeSnapshots : boolean ,
239
+ ) : Promise < boolean > {
240
+ const span = TraceContext . startSpan ( "deleteWorkspaceStorage" , ctx ) ;
241
+ try {
242
+ await this . storageClient . deleteWorkspaceBackups ( ws . ownerId , ws . id , includeSnapshots ) ;
243
+ } catch ( err ) {
244
+ TraceContext . setError ( { span } , err ) ;
245
+ throw err ;
246
+ } finally {
247
+ span . finish ( ) ;
248
+ }
249
+ return true ;
250
+ }
176
251
}
0 commit comments