diff options
Diffstat (limited to 'src/namecache/plugin_namecache_postgres.c')
-rw-r--r-- | src/namecache/plugin_namecache_postgres.c | 280 |
1 files changed, 113 insertions, 167 deletions
diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c index bec8bffd2..d943b0cd8 100644 --- a/src/namecache/plugin_namecache_postgres.c +++ b/src/namecache/plugin_namecache_postgres.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * This file is part of GNUnet | 2 | * This file is part of GNUnet |
3 | * Copyright (C) 2009-2013, 2016 GNUnet e.V. | 3 | * Copyright (C) 2009-2013, 2016, 2017 GNUnet e.V. |
4 | * | 4 | * |
5 | * GNUnet is free software; you can redistribute it and/or modify | 5 | * GNUnet is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published | 6 | * it under the terms of the GNU General Public License as published |
@@ -27,31 +27,10 @@ | |||
27 | #include "gnunet_namecache_plugin.h" | 27 | #include "gnunet_namecache_plugin.h" |
28 | #include "gnunet_namecache_service.h" | 28 | #include "gnunet_namecache_service.h" |
29 | #include "gnunet_gnsrecord_lib.h" | 29 | #include "gnunet_gnsrecord_lib.h" |
30 | #include "gnunet_postgres_lib.h" | ||
31 | #include "gnunet_pq_lib.h" | 30 | #include "gnunet_pq_lib.h" |
32 | #include "namecache.h" | 31 | #include "namecache.h" |
33 | 32 | ||
34 | 33 | ||
35 | /** | ||
36 | * After how many ms "busy" should a DB operation fail for good? | ||
37 | * A low value makes sure that we are more responsive to requests | ||
38 | * (especially PUTs). A high value guarantees a higher success | ||
39 | * rate (SELECTs in iterate can take several seconds despite LIMIT=1). | ||
40 | * | ||
41 | * The default value of 1s should ensure that users do not experience | ||
42 | * huge latencies while at the same time allowing operations to succeed | ||
43 | * with reasonable probability. | ||
44 | */ | ||
45 | #define BUSY_TIMEOUT_MS 1000 | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Log an error message at log-level 'level' that indicates | ||
50 | * a failure of the command 'cmd' on file 'filename' | ||
51 | * with the message given by strerror(errno). | ||
52 | */ | ||
53 | #define LOG_POSTGRES(db, level, cmd) do { GNUNET_log_from (level, "namecache-postgres", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0) | ||
54 | |||
55 | #define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__) | 34 | #define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__) |
56 | 35 | ||
57 | 36 | ||
@@ -72,40 +51,34 @@ struct Plugin | |||
72 | 51 | ||
73 | 52 | ||
74 | /** | 53 | /** |
75 | * Create our database indices. | ||
76 | * | ||
77 | * @param dbh handle to the database | ||
78 | */ | ||
79 | static void | ||
80 | create_indices (PGconn * dbh) | ||
81 | { | ||
82 | /* create indices */ | ||
83 | if ( (GNUNET_OK != | ||
84 | GNUNET_POSTGRES_exec (dbh, | ||
85 | "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) || | ||
86 | (GNUNET_OK != | ||
87 | GNUNET_POSTGRES_exec (dbh, | ||
88 | "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) ) | ||
89 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
90 | _("Failed to create indices\n")); | ||
91 | } | ||
92 | |||
93 | |||
94 | /** | ||
95 | * Initialize the database connections and associated | 54 | * Initialize the database connections and associated |
96 | * data structures (create tables and indices | 55 | * data structures (create tables and indices |
97 | * as needed as well). | 56 | * as needed as well). |
98 | * | 57 | * |
99 | * @param plugin the plugin context (state for this module) | 58 | * @param plugin the plugin context (state for this module) |
100 | * @return GNUNET_OK on success | 59 | * @return #GNUNET_OK on success |
101 | */ | 60 | */ |
102 | static int | 61 | static int |
103 | database_setup (struct Plugin *plugin) | 62 | database_setup (struct Plugin *plugin) |
104 | { | 63 | { |
105 | PGresult *res; | 64 | struct GNUNET_PQ_ExecuteStatement es_temporary = |
106 | 65 | GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns096blocks (" | |
107 | plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg, | 66 | " query BYTEA NOT NULL DEFAULT ''," |
108 | "namecache-postgres"); | 67 | " block BYTEA NOT NULL DEFAULT ''," |
68 | " expiration_time BIGINT NOT NULL DEFAULT 0" | ||
69 | ")" | ||
70 | "WITH OIDS"); | ||
71 | struct GNUNET_PQ_ExecuteStatement es_default = | ||
72 | GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks (" | ||
73 | " query BYTEA NOT NULL DEFAULT ''," | ||
74 | " block BYTEA NOT NULL DEFAULT ''," | ||
75 | " expiration_time BIGINT NOT NULL DEFAULT 0" | ||
76 | ")" | ||
77 | "WITH OIDS"); | ||
78 | const struct GNUNET_PQ_ExecuteStatement *cr; | ||
79 | |||
80 | plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg, | ||
81 | "namecache-postgres"); | ||
109 | if (NULL == plugin->dbh) | 82 | if (NULL == plugin->dbh) |
110 | return GNUNET_SYSERR; | 83 | return GNUNET_SYSERR; |
111 | if (GNUNET_YES == | 84 | if (GNUNET_YES == |
@@ -113,65 +86,56 @@ database_setup (struct Plugin *plugin) | |||
113 | "namecache-postgres", | 86 | "namecache-postgres", |
114 | "TEMPORARY_TABLE")) | 87 | "TEMPORARY_TABLE")) |
115 | { | 88 | { |
116 | res = | 89 | cr = &es_temporary; |
117 | PQexec (plugin->dbh, | ||
118 | "CREATE TEMPORARY TABLE ns096blocks (" | ||
119 | " query BYTEA NOT NULL DEFAULT ''," | ||
120 | " block BYTEA NOT NULL DEFAULT ''," | ||
121 | " expiration_time BIGINT NOT NULL DEFAULT 0" | ||
122 | ")" "WITH OIDS"); | ||
123 | } | 90 | } |
124 | else | 91 | else |
125 | { | 92 | { |
126 | res = | 93 | cr = &es_default; |
127 | PQexec (plugin->dbh, | ||
128 | "CREATE TABLE ns096blocks (" | ||
129 | " query BYTEA NOT NULL DEFAULT ''," | ||
130 | " block BYTEA NOT NULL DEFAULT ''," | ||
131 | " expiration_time BIGINT NOT NULL DEFAULT 0" | ||
132 | ")" "WITH OIDS"); | ||
133 | } | 94 | } |
134 | if ( (NULL == res) || | 95 | |
135 | ((PQresultStatus (res) != PGRES_COMMAND_OK) && | ||
136 | (0 != strcmp ("42P07", /* duplicate table */ | ||
137 | PQresultErrorField | ||
138 | (res, | ||
139 | PG_DIAG_SQLSTATE))))) | ||
140 | { | 96 | { |
141 | (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, | 97 | struct GNUNET_PQ_ExecuteStatement es[] = { |
142 | PGRES_COMMAND_OK, "CREATE TABLE", | 98 | *cr, |
143 | "ns096blocks"); | 99 | GNUNET_PQ_make_try_execute ("CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)"), |
144 | PQfinish (plugin->dbh); | 100 | GNUNET_PQ_make_try_execute ("CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"), |
145 | plugin->dbh = NULL; | 101 | GNUNET_PQ_EXECUTE_STATEMENT_END |
146 | return GNUNET_SYSERR; | 102 | }; |
103 | |||
104 | if (GNUNET_OK != | ||
105 | GNUNET_PQ_exec_statements (plugin->dbh, | ||
106 | es)) | ||
107 | { | ||
108 | PQfinish (plugin->dbh); | ||
109 | plugin->dbh = NULL; | ||
110 | return GNUNET_SYSERR; | ||
111 | } | ||
147 | } | 112 | } |
148 | if (PQresultStatus (res) == PGRES_COMMAND_OK) | 113 | |
149 | create_indices (plugin->dbh); | ||
150 | PQclear (res); | ||
151 | |||
152 | if ((GNUNET_OK != | ||
153 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
154 | "cache_block", | ||
155 | "INSERT INTO ns096blocks (query, block, expiration_time) VALUES " | ||
156 | "($1, $2, $3)", 3)) || | ||
157 | (GNUNET_OK != | ||
158 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
159 | "expire_blocks", | ||
160 | "DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) || | ||
161 | (GNUNET_OK != | ||
162 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
163 | "delete_block", | ||
164 | "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) || | ||
165 | (GNUNET_OK != | ||
166 | GNUNET_POSTGRES_prepare (plugin->dbh, | ||
167 | "lookup_block", | ||
168 | "SELECT block FROM ns096blocks WHERE query=$1" | ||
169 | " ORDER BY expiration_time DESC LIMIT 1", 1))) | ||
170 | { | 114 | { |
171 | PQfinish (plugin->dbh); | 115 | struct GNUNET_PQ_PreparedStatement ps[] = { |
172 | plugin->dbh = NULL; | 116 | GNUNET_PQ_make_prepare ("cache_block", |
173 | return GNUNET_SYSERR; | 117 | "INSERT INTO ns096blocks (query, block, expiration_time) VALUES " |
118 | "($1, $2, $3)", 3), | ||
119 | GNUNET_PQ_make_prepare ("expire_blocks", | ||
120 | "DELETE FROM ns096blocks WHERE expiration_time<$1", 1), | ||
121 | GNUNET_PQ_make_prepare ("delete_block", | ||
122 | "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2), | ||
123 | GNUNET_PQ_make_prepare ("lookup_block", | ||
124 | "SELECT block FROM ns096blocks WHERE query=$1" | ||
125 | " ORDER BY expiration_time DESC LIMIT 1", 1), | ||
126 | GNUNET_PQ_PREPARED_STATEMENT_END | ||
127 | }; | ||
128 | |||
129 | if (GNUNET_OK != | ||
130 | GNUNET_PQ_prepare_statements (plugin->dbh, | ||
131 | ps)) | ||
132 | { | ||
133 | PQfinish (plugin->dbh); | ||
134 | plugin->dbh = NULL; | ||
135 | return GNUNET_SYSERR; | ||
136 | } | ||
174 | } | 137 | } |
138 | |||
175 | return GNUNET_OK; | 139 | return GNUNET_OK; |
176 | } | 140 | } |
177 | 141 | ||
@@ -185,23 +149,16 @@ static void | |||
185 | namecache_postgres_expire_blocks (struct Plugin *plugin) | 149 | namecache_postgres_expire_blocks (struct Plugin *plugin) |
186 | { | 150 | { |
187 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | 151 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); |
188 | struct GNUNET_PQ_QueryParam params[] = { | 152 | struct GNUNET_PQ_QueryParam params[] = { |
189 | GNUNET_PQ_query_param_absolute_time (&now), | 153 | GNUNET_PQ_query_param_absolute_time (&now), |
190 | GNUNET_PQ_query_param_end | 154 | GNUNET_PQ_query_param_end |
191 | }; | 155 | }; |
192 | PGresult *res; | 156 | enum GNUNET_DB_QueryStatus res; |
193 | 157 | ||
194 | res = GNUNET_PQ_exec_prepared (plugin->dbh, | 158 | res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, |
195 | "expire_blocks", | 159 | "expire_blocks", |
196 | params); | 160 | params); |
197 | if (GNUNET_OK != | 161 | GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res); |
198 | GNUNET_POSTGRES_check_result (plugin->dbh, | ||
199 | res, | ||
200 | PGRES_COMMAND_OK, | ||
201 | "PQexecPrepared", | ||
202 | "expire_blocks")) | ||
203 | return; | ||
204 | PQclear (res); | ||
205 | } | 162 | } |
206 | 163 | ||
207 | 164 | ||
@@ -217,24 +174,17 @@ delete_old_block (struct Plugin *plugin, | |||
217 | const struct GNUNET_HashCode *query, | 174 | const struct GNUNET_HashCode *query, |
218 | struct GNUNET_TIME_AbsoluteNBO expiration_time) | 175 | struct GNUNET_TIME_AbsoluteNBO expiration_time) |
219 | { | 176 | { |
220 | struct GNUNET_PQ_QueryParam params[] = { | 177 | struct GNUNET_PQ_QueryParam params[] = { |
221 | GNUNET_PQ_query_param_auto_from_type (query), | 178 | GNUNET_PQ_query_param_auto_from_type (query), |
222 | GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time), | 179 | GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time), |
223 | GNUNET_PQ_query_param_end | 180 | GNUNET_PQ_query_param_end |
224 | }; | 181 | }; |
225 | PGresult *res; | 182 | enum GNUNET_DB_QueryStatus res; |
226 | 183 | ||
227 | res = GNUNET_PQ_exec_prepared (plugin->dbh, | 184 | res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, |
228 | "delete_block", | 185 | "delete_block", |
229 | params); | 186 | params); |
230 | if (GNUNET_OK != | 187 | GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res); |
231 | GNUNET_POSTGRES_check_result (plugin->dbh, | ||
232 | res, | ||
233 | PGRES_COMMAND_OK, | ||
234 | "PQexecPrepared", | ||
235 | "delete_block")) | ||
236 | return; | ||
237 | PQclear (res); | ||
238 | } | 188 | } |
239 | 189 | ||
240 | 190 | ||
@@ -254,13 +204,13 @@ namecache_postgres_cache_block (void *cls, | |||
254 | size_t block_size = ntohl (block->purpose.size) + | 204 | size_t block_size = ntohl (block->purpose.size) + |
255 | sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + | 205 | sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + |
256 | sizeof (struct GNUNET_CRYPTO_EcdsaSignature); | 206 | sizeof (struct GNUNET_CRYPTO_EcdsaSignature); |
257 | struct GNUNET_PQ_QueryParam params[] = { | 207 | struct GNUNET_PQ_QueryParam params[] = { |
258 | GNUNET_PQ_query_param_auto_from_type (&query), | 208 | GNUNET_PQ_query_param_auto_from_type (&query), |
259 | GNUNET_PQ_query_param_fixed_size (block, block_size), | 209 | GNUNET_PQ_query_param_fixed_size (block, block_size), |
260 | GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time), | 210 | GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time), |
261 | GNUNET_PQ_query_param_end | 211 | GNUNET_PQ_query_param_end |
262 | }; | 212 | }; |
263 | PGresult *res; | 213 | enum GNUNET_DB_QueryStatus res; |
264 | 214 | ||
265 | namecache_postgres_expire_blocks (plugin); | 215 | namecache_postgres_expire_blocks (plugin); |
266 | GNUNET_CRYPTO_hash (&block->derived_key, | 216 | GNUNET_CRYPTO_hash (&block->derived_key, |
@@ -271,19 +221,15 @@ namecache_postgres_cache_block (void *cls, | |||
271 | GNUNET_break (0); | 221 | GNUNET_break (0); |
272 | return GNUNET_SYSERR; | 222 | return GNUNET_SYSERR; |
273 | } | 223 | } |
274 | delete_old_block (plugin, &query, block->expiration_time); | 224 | delete_old_block (plugin, |
275 | 225 | &query, | |
276 | res = GNUNET_PQ_exec_prepared (plugin->dbh, | 226 | block->expiration_time); |
277 | "cache_block", | 227 | |
278 | params); | 228 | res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh, |
279 | if (GNUNET_OK != | 229 | "cache_block", |
280 | GNUNET_POSTGRES_check_result (plugin->dbh, | 230 | params); |
281 | res, | 231 | if (0 > res) |
282 | PGRES_COMMAND_OK, | ||
283 | "PQexecPrepared", | ||
284 | "cache_block")) | ||
285 | return GNUNET_SYSERR; | 232 | return GNUNET_SYSERR; |
286 | PQclear (res); | ||
287 | return GNUNET_OK; | 233 | return GNUNET_OK; |
288 | } | 234 | } |
289 | 235 | ||
@@ -301,42 +247,41 @@ namecache_postgres_cache_block (void *cls, | |||
301 | static int | 247 | static int |
302 | namecache_postgres_lookup_block (void *cls, | 248 | namecache_postgres_lookup_block (void *cls, |
303 | const struct GNUNET_HashCode *query, | 249 | const struct GNUNET_HashCode *query, |
304 | GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls) | 250 | GNUNET_NAMECACHE_BlockCallback iter, |
251 | void *iter_cls) | ||
305 | { | 252 | { |
306 | struct Plugin *plugin = cls; | 253 | struct Plugin *plugin = cls; |
307 | struct GNUNET_PQ_QueryParam params[] = { | 254 | size_t bsize; |
255 | struct GNUNET_GNSRECORD_Block *block; | ||
256 | struct GNUNET_PQ_QueryParam params[] = { | ||
308 | GNUNET_PQ_query_param_auto_from_type (query), | 257 | GNUNET_PQ_query_param_auto_from_type (query), |
309 | GNUNET_PQ_query_param_end | 258 | GNUNET_PQ_query_param_end |
310 | }; | 259 | }; |
311 | PGresult *res; | 260 | struct GNUNET_PQ_ResultSpec rs[] = { |
312 | unsigned int cnt; | 261 | GNUNET_PQ_result_spec_variable_size ("block", |
313 | size_t bsize; | 262 | (void **) &block, |
314 | const struct GNUNET_GNSRECORD_Block *block; | 263 | &bsize), |
315 | 264 | GNUNET_PQ_result_spec_end | |
316 | res = GNUNET_PQ_exec_prepared (plugin->dbh, | 265 | }; |
317 | "lookup_block", | 266 | enum GNUNET_DB_QueryStatus res; |
318 | params); | 267 | |
319 | if (GNUNET_OK != | 268 | res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, |
320 | GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, | 269 | "lookup_block", |
321 | "PQexecPrepared", | 270 | params, |
322 | "lookup_block")) | 271 | rs); |
272 | if (0 > res) | ||
323 | { | 273 | { |
324 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 274 | LOG (GNUNET_ERROR_TYPE_WARNING, |
325 | "Failing lookup (postgres error)\n"); | 275 | "Failing lookup block in namecache (postgres error)\n"); |
326 | return GNUNET_SYSERR; | 276 | return GNUNET_SYSERR; |
327 | } | 277 | } |
328 | if (0 == (cnt = PQntuples (res))) | 278 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res) |
329 | { | 279 | { |
330 | /* no result */ | 280 | /* no result */ |
331 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 281 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
332 | "Ending iteration (no more results)\n"); | 282 | "Ending iteration (no more results)\n"); |
333 | PQclear (res); | ||
334 | return GNUNET_NO; | 283 | return GNUNET_NO; |
335 | } | 284 | } |
336 | GNUNET_assert (1 == cnt); | ||
337 | GNUNET_assert (1 != PQnfields (res)); | ||
338 | bsize = PQgetlength (res, 0, 0); | ||
339 | block = (const struct GNUNET_GNSRECORD_Block *) PQgetvalue (res, 0, 0); | ||
340 | if ( (bsize < sizeof (*block)) || | 285 | if ( (bsize < sizeof (*block)) || |
341 | (bsize != ntohl (block->purpose.size) + | 286 | (bsize != ntohl (block->purpose.size) + |
342 | sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + | 287 | sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + |
@@ -345,11 +290,12 @@ namecache_postgres_lookup_block (void *cls, | |||
345 | GNUNET_break (0); | 290 | GNUNET_break (0); |
346 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 291 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
347 | "Failing lookup (corrupt block)\n"); | 292 | "Failing lookup (corrupt block)\n"); |
348 | PQclear (res); | 293 | GNUNET_PQ_cleanup_result (rs); |
349 | return GNUNET_SYSERR; | 294 | return GNUNET_SYSERR; |
350 | } | 295 | } |
351 | iter (iter_cls, block); | 296 | iter (iter_cls, |
352 | PQclear (res); | 297 | block); |
298 | GNUNET_PQ_cleanup_result (rs); | ||
353 | return GNUNET_OK; | 299 | return GNUNET_OK; |
354 | } | 300 | } |
355 | 301 | ||
@@ -395,7 +341,7 @@ libgnunet_plugin_namecache_postgres_init (void *cls) | |||
395 | api->cache_block = &namecache_postgres_cache_block; | 341 | api->cache_block = &namecache_postgres_cache_block; |
396 | api->lookup_block = &namecache_postgres_lookup_block; | 342 | api->lookup_block = &namecache_postgres_lookup_block; |
397 | LOG (GNUNET_ERROR_TYPE_INFO, | 343 | LOG (GNUNET_ERROR_TYPE_INFO, |
398 | _("Postgres database running\n")); | 344 | "Postgres namecache plugin running\n"); |
399 | return api; | 345 | return api; |
400 | } | 346 | } |
401 | 347 | ||
@@ -416,7 +362,7 @@ libgnunet_plugin_namecache_postgres_done (void *cls) | |||
416 | plugin->cfg = NULL; | 362 | plugin->cfg = NULL; |
417 | GNUNET_free (api); | 363 | GNUNET_free (api); |
418 | LOG (GNUNET_ERROR_TYPE_DEBUG, | 364 | LOG (GNUNET_ERROR_TYPE_DEBUG, |
419 | "postgres plugin is finished\n"); | 365 | "Postgres namecache plugin is finished\n"); |
420 | return NULL; | 366 | return NULL; |
421 | } | 367 | } |
422 | 368 | ||