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