7
7
Code related to permanently deleting projects.
8
8
*/
9
9
10
+ import getLogger from "@cocalc/backend/logger" ;
11
+ import getPool from "@cocalc/database/pool" ;
10
12
import { callback2 } from "@cocalc/util/async-utils" ;
11
13
import { PostgreSQL } from "./types" ;
14
+ import { minutes_ago } from "@cocalc/util/misc" ;
15
+ import { getServerSettings } from "@cocalc/database/settings" ;
16
+ import { KUCALC_ON_PREMISES } from "@cocalc/util/db-schema/site-defaults" ;
17
+
18
+ const log = getLogger ( "db:delete-projects" ) ;
12
19
13
20
/*
14
21
Permanently delete from the database all project records, where the
@@ -20,7 +27,7 @@ later then purges these projects from disk as well as the database.
20
27
*/
21
28
export async function permanently_unlink_all_deleted_projects_of_user (
22
29
db : PostgreSQL ,
23
- account_id_or_email_address : string
30
+ account_id_or_email_address : string ,
24
31
) : Promise < void > {
25
32
// Get the account_id if necessary.
26
33
const account_id = await get_account_id ( db , account_id_or_email_address ) ;
@@ -36,7 +43,7 @@ export async function permanently_unlink_all_deleted_projects_of_user(
36
43
37
44
async function get_account_id (
38
45
db : PostgreSQL ,
39
- account_id_or_email_address : string
46
+ account_id_or_email_address : string ,
40
47
) : Promise < string > {
41
48
if ( account_id_or_email_address . indexOf ( "@" ) == - 1 ) {
42
49
return account_id_or_email_address ;
@@ -57,7 +64,7 @@ Another task has to run to actually get rid of the data, etc.
57
64
*/
58
65
export async function unlink_old_deleted_projects (
59
66
db : PostgreSQL ,
60
- age_d = 30
67
+ age_d = 30 ,
61
68
) : Promise < void > {
62
69
await callback2 ( db . _query , {
63
70
query : "UPDATE projects" ,
@@ -69,3 +76,77 @@ export async function unlink_old_deleted_projects(
69
76
] ,
70
77
} ) ;
71
78
}
79
+
80
+ const Q_CLEANUP_SYNCSTRINGS = `
81
+ SELECT p.project_id, s.string_id
82
+ FROM projects as p
83
+ INNER JOIN syncstrings as s
84
+ ON p.project_id = s.project_id
85
+ WHERE p.deleted = true
86
+ AND p.state ->> 'state' != 'deleted'
87
+ ` ;
88
+
89
+ /*
90
+ This is more thorough than the above. It issues actual delete operations on data of projects marked as deleted.
91
+ When done, it sets the state.state to "deleted".
92
+
93
+ The operations involves deleting all syncstrings of that project (and associated with that, patches),
94
+ and only for on-prem setups, it also deletes all the data stored in the project on disk.
95
+
96
+ This function is called every couple of hours. Hence ensure it does not run longer than the given max_run_m time (minutes)
97
+ */
98
+ export async function cleanup_old_projects_data (
99
+ db : PostgreSQL ,
100
+ delay_ms = 50 ,
101
+ max_run_m = 60 ,
102
+ ) {
103
+ const settings = await getServerSettings ( ) ;
104
+ const on_prem = settings . kucalc === KUCALC_ON_PREMISES ;
105
+
106
+ log . debug ( "cleanup_old_projects_data" , { delay_ms, max_run_m, on_prem } ) ;
107
+ const start_ts = new Date ( ) ;
108
+
109
+ const pool = getPool ( ) ;
110
+ const { rows } = await pool . query ( Q_CLEANUP_SYNCSTRINGS ) ;
111
+
112
+ let num = 0 ;
113
+ let pid = "" ;
114
+
115
+ for ( const row of rows ) {
116
+ const { project_id, string_id } = row ;
117
+ if ( start_ts < minutes_ago ( max_run_m ) ) {
118
+ log . debug (
119
+ `cleanup_old_projects_data: too much time elapsed, breaking after ${ num } syncstrings` ,
120
+ ) ;
121
+ break ;
122
+ }
123
+
124
+ log . debug (
125
+ `cleanup_old_projects_data: deleting syncstring ${ project_id } /${ string_id } ` ,
126
+ ) ;
127
+ num += 1 ;
128
+ await callback2 ( db . delete_syncstring , { string_id } ) ;
129
+
130
+ // wait for the given amount of delay_ms millio seconds
131
+ await new Promise ( ( done ) => setTimeout ( done , delay_ms ) ) ;
132
+
133
+ if ( pid != project_id ) {
134
+ pid = project_id ;
135
+ if ( on_prem ) {
136
+ log . debug (
137
+ `cleanup_old_projects_data: deleting project data in ${ project_id } ` ,
138
+ ) ;
139
+ // TODO: this only works on-prem, and requires the project files to be mounted
140
+
141
+ log . debug ( `deleting all shared files in project ${ project_id } ` ) ;
142
+ // TODO: do it directly like above, and also get rid of all those shares in the database
143
+ }
144
+
145
+ // now, that we're done with that project, mark it as state.state ->> 'deleted'
146
+ await callback2 ( db . set_project_state , {
147
+ project_id,
148
+ state : "deleted" ,
149
+ } ) ;
150
+ }
151
+ }
152
+ }
0 commit comments