aboutsummaryrefslogtreecommitdiff
path: root/src/datastore/plugin_datastore_postgres.c
diff options
context:
space:
mode:
authorDavid Barksdale <amatus@amat.us>2017-03-19 15:55:32 -0500
committerDavid Barksdale <amatus@amat.us>2017-03-19 17:38:36 -0500
commit2dde0202c5590eeb051c1346f2b66293d83b87ce (patch)
tree7997191912ee4c70959934d6c9783a0c9f450fec /src/datastore/plugin_datastore_postgres.c
parentd17d833dfd93a81f3540d472d1be4dfb7e9cbd03 (diff)
downloadgnunet-2dde0202c5590eeb051c1346f2b66293d83b87ce.tar.gz
gnunet-2dde0202c5590eeb051c1346f2b66293d83b87ce.zip
[datastore] Fix #3743
This change adds support for key == NULL to the datastore plugins and replaces the offset argument with a next_uid and random arguments to increase performance in the key == NULL case. With the offset argument a datastore plugin would have to count all matching keys before fetching the key at the right offset, which would iterate over the entire database in the case of key == NULL. The offset argument was used in two ways: to iterate over a set of matching values and to start iteration at a random matching value. The new API seperates these into two arguments: if random is true it will return a random matching value, otherwise next_uid can be set to uid + 1 to return the next matching value. The random argument was not added to get_zero_anonymity. This function is used to periodically insert zero anonymity values into the DHT. I don't think it's necessary to randomize this.
Diffstat (limited to 'src/datastore/plugin_datastore_postgres.c')
-rw-r--r--src/datastore/plugin_datastore_postgres.c233
1 files changed, 53 insertions, 180 deletions
diff --git a/src/datastore/plugin_datastore_postgres.c b/src/datastore/plugin_datastore_postgres.c
index 8b8737935..0376ebb6c 100644
--- a/src/datastore/plugin_datastore_postgres.c
+++ b/src/datastore/plugin_datastore_postgres.c
@@ -80,6 +80,7 @@ init_connection (struct Plugin *plugin)
80 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel 80 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
81 * we do math or inequality tests, so we can't handle the entire range of uint32_t. 81 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
82 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC. 82 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
83 * PostgreSQL also recommends against using WITH OIDS.
83 */ 84 */
84 ret = 85 ret =
85 PQexec (plugin->dbh, 86 PQexec (plugin->dbh,
@@ -176,40 +177,18 @@ init_connection (struct Plugin *plugin)
176 } 177 }
177 PQclear (ret); 178 PQclear (ret);
178 if ((GNUNET_OK != 179 if ((GNUNET_OK !=
179 GNUNET_POSTGRES_prepare (plugin->dbh, "getvt",
180 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
181 "WHERE hash=$1 AND vhash=$2 AND type=$3 "
182 "ORDER BY oid ASC LIMIT 1 OFFSET $4", 4)) ||
183 (GNUNET_OK !=
184 GNUNET_POSTGRES_prepare (plugin->dbh, "gett",
185 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
186 "WHERE hash=$1 AND type=$2 "
187 "ORDER BY oid ASC LIMIT 1 OFFSET $3", 3)) ||
188 (GNUNET_OK !=
189 GNUNET_POSTGRES_prepare (plugin->dbh, "getv",
190 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
191 "WHERE hash=$1 AND vhash=$2 "
192 "ORDER BY oid ASC LIMIT 1 OFFSET $3", 3)) ||
193 (GNUNET_OK !=
194 GNUNET_POSTGRES_prepare (plugin->dbh, "get", 180 GNUNET_POSTGRES_prepare (plugin->dbh, "get",
195 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 " 181 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
196 "WHERE hash=$1 " "ORDER BY oid ASC LIMIT 1 OFFSET $2", 2)) || 182 "WHERE oid >= $1::bigint AND "
197 (GNUNET_OK != 183 "(rvalue >= $2 OR 0 = $3::smallint) AND "
198 GNUNET_POSTGRES_prepare (plugin->dbh, "count_getvt", 184 "(hash = $4 OR 0 = $5::smallint) AND "
199 "SELECT count(*) FROM gn090 WHERE hash=$1 AND vhash=$2 AND type=$3", 3)) || 185 "(vhash = $6 OR 0 = $7::smallint) AND "
200 (GNUNET_OK != 186 "(type = $8 OR 0 = $9::smallint) "
201 GNUNET_POSTGRES_prepare (plugin->dbh, "count_gett", 187 "ORDER BY oid ASC LIMIT 1", 9)) ||
202 "SELECT count(*) FROM gn090 WHERE hash=$1 AND type=$2", 2)) ||
203 (GNUNET_OK !=
204 GNUNET_POSTGRES_prepare (plugin->dbh, "count_getv",
205 "SELECT count(*) FROM gn090 WHERE hash=$1 AND vhash=$2", 2)) ||
206 (GNUNET_OK !=
207 GNUNET_POSTGRES_prepare (plugin->dbh, "count_get",
208 "SELECT count(*) FROM gn090 WHERE hash=$1", 1)) ||
209 (GNUNET_OK != 188 (GNUNET_OK !=
210 GNUNET_POSTGRES_prepare (plugin->dbh, "put", 189 GNUNET_POSTGRES_prepare (plugin->dbh, "put",
211 "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) " 190 "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
212 "VALUES ($1, $2, $3, $4, $5, RANDOM(), $6, $7, $8)", 9)) || 191 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", 9)) ||
213 (GNUNET_OK != 192 (GNUNET_OK !=
214 GNUNET_POSTGRES_prepare (plugin->dbh, "update", 193 GNUNET_POSTGRES_prepare (plugin->dbh, "update",
215 "UPDATE gn090 SET prio = prio + $1, expire = CASE WHEN expire < $2 THEN $2 ELSE expire END " 194 "UPDATE gn090 SET prio = prio + $1, expire = CASE WHEN expire < $2 THEN $2 ELSE expire END "
@@ -221,8 +200,9 @@ init_connection (struct Plugin *plugin)
221 (GNUNET_OK != 200 (GNUNET_OK !=
222 GNUNET_POSTGRES_prepare (plugin->dbh, "select_non_anonymous", 201 GNUNET_POSTGRES_prepare (plugin->dbh, "select_non_anonymous",
223 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 " 202 "SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
224 "WHERE anonLevel = 0 AND type = $1 ORDER BY oid DESC LIMIT 1 OFFSET $2", 203 "WHERE anonLevel = 0 AND type = $1 AND oid >= $2::bigint "
225 1)) || 204 "ORDER BY oid ASC LIMIT 1",
205 2)) ||
226 (GNUNET_OK != 206 (GNUNET_OK !=
227 GNUNET_POSTGRES_prepare (plugin->dbh, "select_expiration_order", 207 GNUNET_POSTGRES_prepare (plugin->dbh, "select_expiration_order",
228 "(SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 " 208 "(SELECT type, prio, anonLevel, expire, hash, value, oid FROM gn090 "
@@ -328,6 +308,8 @@ postgres_plugin_put (void *cls,
328 struct Plugin *plugin = cls; 308 struct Plugin *plugin = cls;
329 uint32_t utype = type; 309 uint32_t utype = type;
330 struct GNUNET_HashCode vhash; 310 struct GNUNET_HashCode vhash;
311 uint64_t rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
312 UINT64_MAX);
331 PGresult *ret; 313 PGresult *ret;
332 struct GNUNET_PQ_QueryParam params[] = { 314 struct GNUNET_PQ_QueryParam params[] = {
333 GNUNET_PQ_query_param_uint32 (&replication), 315 GNUNET_PQ_query_param_uint32 (&replication),
@@ -335,6 +317,7 @@ postgres_plugin_put (void *cls,
335 GNUNET_PQ_query_param_uint32 (&priority), 317 GNUNET_PQ_query_param_uint32 (&priority),
336 GNUNET_PQ_query_param_uint32 (&anonymity), 318 GNUNET_PQ_query_param_uint32 (&anonymity),
337 GNUNET_PQ_query_param_absolute_time (&expiration), 319 GNUNET_PQ_query_param_absolute_time (&expiration),
320 GNUNET_PQ_query_param_uint64 (&rvalue),
338 GNUNET_PQ_query_param_auto_from_type (key), 321 GNUNET_PQ_query_param_auto_from_type (key),
339 GNUNET_PQ_query_param_auto_from_type (&vhash), 322 GNUNET_PQ_query_param_auto_from_type (&vhash),
340 GNUNET_PQ_query_param_fixed_size (data, size), 323 GNUNET_PQ_query_param_fixed_size (data, size),
@@ -495,12 +478,11 @@ process_result (struct Plugin *plugin,
495 478
496 479
497/** 480/**
498 * Iterate over the results for a particular key 481 * Get one of the results for a particular key in the datastore.
499 * in the datastore.
500 * 482 *
501 * @param cls closure with the 'struct Plugin' 483 * @param cls closure with the 'struct Plugin'
502 * @param offset offset of the result (modulo num-results); 484 * @param next_uid return the result with lowest uid >= next_uid
503 * specific ordering does not matter for the offset 485 * @param random if true, return a random result instead of using next_uid
504 * @param key maybe NULL (to match all entries) 486 * @param key maybe NULL (to match all entries)
505 * @param vhash hash of the value, maybe NULL (to 487 * @param vhash hash of the value, maybe NULL (to
506 * match all values that have the right key). 488 * match all values that have the right key).
@@ -510,160 +492,52 @@ process_result (struct Plugin *plugin,
510 * @param type entries of which type are relevant? 492 * @param type entries of which type are relevant?
511 * Use 0 for any type. 493 * Use 0 for any type.
512 * @param proc function to call on the matching value; 494 * @param proc function to call on the matching value;
513 * will be called once with a NULL if no value matches 495 * will be called with NULL if nothing matches
514 * @param proc_cls closure for iter 496 * @param proc_cls closure for @a proc
515 */ 497 */
516static void 498static void
517postgres_plugin_get_key (void *cls, 499postgres_plugin_get_key (void *cls,
518 uint64_t offset, 500 uint64_t next_uid,
501 bool random,
519 const struct GNUNET_HashCode *key, 502 const struct GNUNET_HashCode *key,
520 const struct GNUNET_HashCode *vhash, 503 const struct GNUNET_HashCode *vhash,
521 enum GNUNET_BLOCK_Type type, 504 enum GNUNET_BLOCK_Type type,
522 PluginDatumProcessor proc, 505 PluginDatumProcessor proc,
523 void *proc_cls) 506 void *proc_cls)
524{ 507{
525 struct Plugin *plugin = cls; 508 struct Plugin *plugin = cls;
526 uint32_t utype = type; 509 uint32_t utype = type;
510 uint16_t use_rvalue = random;
511 uint16_t use_key = NULL != key;
512 uint16_t use_vhash = NULL != vhash;
513 uint16_t use_type = GNUNET_BLOCK_TYPE_ANY != type;
514 uint64_t rvalue;
515 struct GNUNET_PQ_QueryParam params[] = {
516 GNUNET_PQ_query_param_uint64 (&next_uid),
517 GNUNET_PQ_query_param_uint64 (&rvalue),
518 GNUNET_PQ_query_param_uint16 (&use_rvalue),
519 GNUNET_PQ_query_param_auto_from_type (key),
520 GNUNET_PQ_query_param_uint16 (&use_key),
521 GNUNET_PQ_query_param_auto_from_type (vhash),
522 GNUNET_PQ_query_param_uint16 (&use_vhash),
523 GNUNET_PQ_query_param_uint32 (&utype),
524 GNUNET_PQ_query_param_uint16 (&use_type),
525 GNUNET_PQ_query_param_end
526 };
527 PGresult *ret; 527 PGresult *ret;
528 uint64_t total;
529 uint64_t limit_off;
530 528
531 if (0 != type) 529 if (random)
532 { 530 {
533 if (NULL != vhash) 531 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
534 { 532 UINT64_MAX);
535 struct GNUNET_PQ_QueryParam params[] = { 533 next_uid = 0;
536 GNUNET_PQ_query_param_auto_from_type (key),
537 GNUNET_PQ_query_param_auto_from_type (vhash),
538 GNUNET_PQ_query_param_uint32 (&utype),
539 GNUNET_PQ_query_param_end
540 };
541 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
542 "count_getvt",
543 params);
544 }
545 else
546 {
547 struct GNUNET_PQ_QueryParam params[] = {
548 GNUNET_PQ_query_param_auto_from_type (key),
549 GNUNET_PQ_query_param_uint32 (&utype),
550 GNUNET_PQ_query_param_end
551 };
552 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
553 "count_gett",
554 params);
555 }
556 } 534 }
557 else 535 else
558 { 536 rvalue = 0;
559 if (NULL != vhash)
560 {
561 struct GNUNET_PQ_QueryParam params[] = {
562 GNUNET_PQ_query_param_auto_from_type (key),
563 GNUNET_PQ_query_param_auto_from_type (vhash),
564 GNUNET_PQ_query_param_end
565 };
566 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
567 "count_getv",
568 params);
569 }
570 else
571 {
572 struct GNUNET_PQ_QueryParam params[] = {
573 GNUNET_PQ_query_param_auto_from_type (key),
574 GNUNET_PQ_query_param_end
575 };
576 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
577 "count_get",
578 params);
579 }
580 }
581 537
582 if (GNUNET_OK != 538 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
583 GNUNET_POSTGRES_check_result (plugin->dbh, 539 "get",
584 ret, 540 params);
585 PGRES_TUPLES_OK,
586 "PQexecParams",
587 "count"))
588 {
589 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
590 GNUNET_TIME_UNIT_ZERO_ABS, 0);
591 return;
592 }
593 if ( (PQntuples (ret) != 1) ||
594 (PQnfields (ret) != 1) ||
595 (PQgetlength (ret, 0, 0) != sizeof (uint64_t)))
596 {
597 GNUNET_break (0);
598 PQclear (ret);
599 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
600 GNUNET_TIME_UNIT_ZERO_ABS, 0);
601 return;
602 }
603 total = GNUNET_ntohll (*(const uint64_t *) PQgetvalue (ret, 0, 0));
604 PQclear (ret);
605 if (0 == total)
606 {
607 proc (proc_cls, NULL, 0, NULL, 0, 0, 0,
608 GNUNET_TIME_UNIT_ZERO_ABS, 0);
609 return;
610 }
611 limit_off = offset % total;
612
613 if (0 != type)
614 {
615 if (NULL != vhash)
616 {
617 struct GNUNET_PQ_QueryParam params[] = {
618 GNUNET_PQ_query_param_auto_from_type (key),
619 GNUNET_PQ_query_param_auto_from_type (vhash),
620 GNUNET_PQ_query_param_uint32 (&utype),
621 GNUNET_PQ_query_param_uint64 (&limit_off),
622 GNUNET_PQ_query_param_end
623 };
624 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
625 "getvt",
626 params);
627 }
628 else
629 {
630 struct GNUNET_PQ_QueryParam params[] = {
631 GNUNET_PQ_query_param_auto_from_type (key),
632 GNUNET_PQ_query_param_uint32 (&utype),
633 GNUNET_PQ_query_param_uint64 (&limit_off),
634 GNUNET_PQ_query_param_end
635 };
636 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
637 "gett",
638 params);
639 }
640 }
641 else
642 {
643 if (NULL != vhash)
644 {
645 struct GNUNET_PQ_QueryParam params[] = {
646 GNUNET_PQ_query_param_auto_from_type (key),
647 GNUNET_PQ_query_param_auto_from_type (vhash),
648 GNUNET_PQ_query_param_uint64 (&limit_off),
649 GNUNET_PQ_query_param_end
650 };
651 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
652 "getv",
653 params);
654 }
655 else
656 {
657 struct GNUNET_PQ_QueryParam params[] = {
658 GNUNET_PQ_query_param_auto_from_type (key),
659 GNUNET_PQ_query_param_uint64 (&limit_off),
660 GNUNET_PQ_query_param_end
661 };
662 ret = GNUNET_PQ_exec_prepared (plugin->dbh,
663 "get",
664 params);
665 }
666 }
667 process_result (plugin, 541 process_result (plugin,
668 proc, 542 proc,
669 proc_cls, 543 proc_cls,
@@ -677,26 +551,25 @@ postgres_plugin_get_key (void *cls,
677 * the given iterator for each of them. 551 * the given iterator for each of them.
678 * 552 *
679 * @param cls our `struct Plugin *` 553 * @param cls our `struct Plugin *`
680 * @param offset offset of the result (modulo num-results); 554 * @param next_uid return the result with lowest uid >= next_uid
681 * specific ordering does not matter for the offset
682 * @param type entries of which type should be considered? 555 * @param type entries of which type should be considered?
683 * Use 0 for any type. 556 * Must not be zero (ANY).
684 * @param proc function to call on the matching value; 557 * @param proc function to call on the matching value;
685 * will be called with a NULL if no value matches 558 * will be called with NULL if no value matches
686 * @param proc_cls closure for @a proc 559 * @param proc_cls closure for @a proc
687 */ 560 */
688static void 561static void
689postgres_plugin_get_zero_anonymity (void *cls, 562postgres_plugin_get_zero_anonymity (void *cls,
690 uint64_t offset, 563 uint64_t next_uid,
691 enum GNUNET_BLOCK_Type type, 564 enum GNUNET_BLOCK_Type type,
692 PluginDatumProcessor proc, 565 PluginDatumProcessor proc,
693 void *proc_cls) 566 void *proc_cls)
694{ 567{
695 struct Plugin *plugin = cls; 568 struct Plugin *plugin = cls;
696 uint32_t utype = type; 569 uint32_t utype = type;
697 struct GNUNET_PQ_QueryParam params[] = { 570 struct GNUNET_PQ_QueryParam params[] = {
698 GNUNET_PQ_query_param_uint32 (&utype), 571 GNUNET_PQ_query_param_uint32 (&utype),
699 GNUNET_PQ_query_param_uint64 (&offset), 572 GNUNET_PQ_query_param_uint64 (&next_uid),
700 GNUNET_PQ_query_param_end 573 GNUNET_PQ_query_param_end
701 }; 574 };
702 PGresult *ret; 575 PGresult *ret;