LCOV - code coverage report
Current view: top level - exchangedb - test_work_shards.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 61 61
Test Date: 2026-09-11 18:55:36 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2026 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file exchangedb/test_work_shards.c
      18              :  * @brief tests for the exchangedb functions whose primary table is
      19              :  *        `work_shards`
      20              :  * @author Christian Grothoff
      21              :  *
      22              :  * Covers #TALER_EXCHANGEDB_begin_shard(),
      23              :  * #TALER_EXCHANGEDB_update_shard_progress(),
      24              :  * #TALER_EXCHANGEDB_abort_shard() and, for its `work_shards` half,
      25              :  * #TALER_EXCHANGEDB_delete_shard_locks().
      26              :  *
      27              :  * `work_shards` has no foreign keys.  Every check uses a job name of its
      28              :  * own so that the shard numbering of one check cannot confuse the next; the
      29              :  * delete-locks check runs last because it empties the table.
      30              :  */
      31              : #include "test_common.h"
      32              : #include "exchange-database/abort_shard.h"
      33              : #include "exchange-database/begin_shard.h"
      34              : #include "exchange-database/delete_shard_locks.h"
      35              : #include "exchange-database/update_shard_progress.h"
      36              : 
      37              : 
      38              : /**
      39              :  * Lease we ask for; long enough that no check can lose its shard while it
      40              :  * is running.
      41              :  */
      42              : #define LEASE GNUNET_TIME_UNIT_HOURS
      43              : 
      44              : 
      45              : /**
      46              :  * Nothing to abort and no progress to record while the table is empty.
      47              :  *
      48              :  * @param pg the database context
      49              :  * @return 0 on success
      50              :  */
      51              : static int
      52            1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
      53              : {
      54            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      55              :           TALER_EXCHANGEDB_abort_shard (pg,
      56              :                                         "ws-nothing",
      57              :                                         0,
      58              :                                         10));
      59            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
      60              :           TALER_EXCHANGEDB_update_shard_progress (pg,
      61              :                                                   "ws-nothing",
      62              :                                                   0,
      63              :                                                   10,
      64              :                                                   5,
      65              :                                                   LEASE));
      66            1 :   FAILIF (0 != TDB_count (pg,
      67              :                           "FROM work_shards"));
      68            1 :   return 0;
      69              : }
      70              : 
      71              : 
      72              : /**
      73              :  * The first shard of a job starts at 0, and a job whose only shard is
      74              :  * leased gets the next range rather than the leased one.
      75              :  *
      76              :  * @param pg the database context
      77              :  * @return 0 on success
      78              :  */
      79              : static int
      80            1 : check_begin (struct TALER_EXCHANGEDB_PostgresContext *pg)
      81              : {
      82              :   uint64_t start;
      83              :   uint64_t end;
      84              :   uint64_t progress;
      85              : 
      86            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
      87              :           TALER_EXCHANGEDB_begin_shard (pg,
      88              :                                         "ws-begin",
      89              :                                         LEASE,
      90              :                                         100,
      91              :                                         &start,
      92              :                                         &end,
      93              :                                         &progress));
      94            1 :   FAILIF (0 != start);
      95            1 :   FAILIF (100 != end);
      96            1 :   FAILIF (0 != progress);
      97              : 
      98              :   /* the shard we just took is leased, so this must open the next one */
      99            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     100              :           TALER_EXCHANGEDB_begin_shard (pg,
     101              :                                         "ws-begin",
     102              :                                         LEASE,
     103              :                                         100,
     104              :                                         &start,
     105              :                                         &end,
     106              :                                         &progress));
     107            1 :   FAILIF (100 != start);
     108            1 :   FAILIF (200 != end);
     109            1 :   FAILIF (100 != progress);
     110            1 :   FAILIF (2 != TDB_count (pg,
     111              :                           "FROM work_shards"
     112              :                           " WHERE job_name='ws-begin'"));
     113              :   /* a different job has its own numbering */
     114            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     115              :           TALER_EXCHANGEDB_begin_shard (pg,
     116              :                                         "ws-begin-other",
     117              :                                         LEASE,
     118              :                                         7,
     119              :                                         &start,
     120              :                                         &end,
     121              :                                         &progress));
     122            1 :   FAILIF (0 != start);
     123            1 :   FAILIF (7 != end);
     124            1 :   return 0;
     125              : }
     126              : 
     127              : 
     128              : /**
     129              :  * Progress is recorded, never rewound, and completes the shard exactly
     130              :  * when it reaches the end of the range.
     131              :  *
     132              :  * @param pg the database context
     133              :  * @return 0 on success
     134              :  */
     135              : static int
     136            1 : check_progress (struct TALER_EXCHANGEDB_PostgresContext *pg)
     137              : {
     138              :   uint64_t start;
     139              :   uint64_t end;
     140              :   uint64_t progress;
     141              : 
     142            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     143              :           TALER_EXCHANGEDB_begin_shard (pg,
     144              :                                         "ws-progress",
     145              :                                         LEASE,
     146              :                                         100,
     147              :                                         &start,
     148              :                                         &end,
     149              :                                         &progress));
     150            1 :   FAILIF (0 != start);
     151              : 
     152              :   /* partial progress leaves the shard open */
     153            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     154              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     155              :                                                   "ws-progress",
     156              :                                                   0,
     157              :                                                   100,
     158              :                                                   50,
     159              :                                                   LEASE));
     160            1 :   FAILIF (1 != TDB_count (pg,
     161              :                           "FROM work_shards"
     162              :                           " WHERE job_name='ws-progress'"
     163              :                           "   AND progress_row=50"
     164              :                           "   AND NOT completed"));
     165              : 
     166              :   /* ...and cannot be pushed back by a straggler */
     167            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     168              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     169              :                                                   "ws-progress",
     170              :                                                   0,
     171              :                                                   100,
     172              :                                                   30,
     173              :                                                   LEASE));
     174            1 :   FAILIF (1 != TDB_count (pg,
     175              :                           "FROM work_shards"
     176              :                           " WHERE job_name='ws-progress'"
     177              :                           "   AND progress_row=50"));
     178              : 
     179              :   /* reaching the end completes it */
     180            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     181              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     182              :                                                   "ws-progress",
     183              :                                                   0,
     184              :                                                   100,
     185              :                                                   100,
     186              :                                                   LEASE));
     187            1 :   FAILIF (1 != TDB_count (pg,
     188              :                           "FROM work_shards"
     189              :                           " WHERE job_name='ws-progress'"
     190              :                           "   AND completed"));
     191              : 
     192              :   /* a shard range that does not exist is not silently created */
     193            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     194              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     195              :                                                   "ws-progress",
     196              :                                                   1000,
     197              :                                                   1100,
     198              :                                                   1050,
     199              :                                                   LEASE));
     200              :   /* neither is one belonging to another job */
     201            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     202              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     203              :                                                   "ws-progress-elsewhere",
     204              :                                                   0,
     205              :                                                   100,
     206              :                                                   100,
     207              :                                                   LEASE));
     208            1 :   FAILIF (1 != TDB_count (pg,
     209              :                           "FROM work_shards"
     210              :                           " WHERE job_name='ws-progress'"));
     211            1 :   return 0;
     212              : }
     213              : 
     214              : 
     215              : /**
     216              :  * An aborted shard is handed out again, together with the progress the
     217              :  * previous worker had committed.  A completed shard is not.
     218              :  *
     219              :  * @param pg the database context
     220              :  * @return 0 on success
     221              :  */
     222              : static int
     223            1 : check_abort_and_takeover (struct TALER_EXCHANGEDB_PostgresContext *pg)
     224              : {
     225              :   uint64_t start;
     226              :   uint64_t end;
     227              :   uint64_t progress;
     228              : 
     229            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     230              :           TALER_EXCHANGEDB_begin_shard (pg,
     231              :                                         "ws-abort",
     232              :                                         LEASE,
     233              :                                         10,
     234              :                                         &start,
     235              :                                         &end,
     236              :                                         &progress));
     237            1 :   FAILIF (0 != start);
     238            1 :   FAILIF (10 != end);
     239            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     240              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     241              :                                                   "ws-abort",
     242              :                                                   0,
     243              :                                                   10,
     244              :                                                   4,
     245              :                                                   LEASE));
     246              : 
     247              :   /* aborting a range that does not exist changes nothing */
     248            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     249              :           TALER_EXCHANGEDB_abort_shard (pg,
     250              :                                         "ws-abort",
     251              :                                         0,
     252              :                                         11));
     253            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
     254              :           TALER_EXCHANGEDB_abort_shard (pg,
     255              :                                         "ws-abort-elsewhere",
     256              :                                         0,
     257              :                                         10));
     258              : 
     259            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     260              :           TALER_EXCHANGEDB_abort_shard (pg,
     261              :                                         "ws-abort",
     262              :                                         0,
     263              :                                         10));
     264              :   /* the abandoned shard comes back, resuming where it was left off */
     265            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     266              :           TALER_EXCHANGEDB_begin_shard (pg,
     267              :                                         "ws-abort",
     268              :                                         LEASE,
     269              :                                         10,
     270              :                                         &start,
     271              :                                         &end,
     272              :                                         &progress));
     273            1 :   FAILIF (0 != start);
     274            1 :   FAILIF (10 != end);
     275            1 :   FAILIF (4 != progress);
     276            1 :   FAILIF (1 != TDB_count (pg,
     277              :                           "FROM work_shards"
     278              :                           " WHERE job_name='ws-abort'"));
     279              : 
     280              :   /* once completed, aborting it still works but it is never handed out */
     281            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     282              :           TALER_EXCHANGEDB_update_shard_progress (pg,
     283              :                                                   "ws-abort",
     284              :                                                   0,
     285              :                                                   10,
     286              :                                                   10,
     287              :                                                   LEASE));
     288            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     289              :           TALER_EXCHANGEDB_abort_shard (pg,
     290              :                                         "ws-abort",
     291              :                                         0,
     292              :                                         10));
     293            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     294              :           TALER_EXCHANGEDB_begin_shard (pg,
     295              :                                         "ws-abort",
     296              :                                         LEASE,
     297              :                                         10,
     298              :                                         &start,
     299              :                                         &end,
     300              :                                         &progress));
     301            1 :   FAILIF (10 != start);
     302            1 :   FAILIF (20 != end);
     303            1 :   return 0;
     304              : }
     305              : 
     306              : 
     307              : /**
     308              :  * Deleting the shard locks empties the table and restarts the numbering.
     309              :  * Runs last, as it discards what the other checks built.
     310              :  *
     311              :  * @param pg the database context
     312              :  * @return 0 on success
     313              :  */
     314              : static int
     315            1 : check_delete_locks (struct TALER_EXCHANGEDB_PostgresContext *pg)
     316              : {
     317              :   uint64_t start;
     318              :   uint64_t end;
     319              :   uint64_t progress;
     320              : 
     321            1 :   FAILIF (0 == TDB_count (pg,
     322              :                           "FROM work_shards"));
     323            1 :   FAILIF (GNUNET_OK !=
     324              :           TALER_EXCHANGEDB_delete_shard_locks (pg));
     325            1 :   FAILIF (0 != TDB_count (pg,
     326              :                           "FROM work_shards"));
     327              :   /* ...and on an already empty table it is a no-op, not an error */
     328            1 :   FAILIF (GNUNET_OK !=
     329              :           TALER_EXCHANGEDB_delete_shard_locks (pg));
     330              : 
     331            1 :   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     332              :           TALER_EXCHANGEDB_begin_shard (pg,
     333              :                                         "ws-begin",
     334              :                                         LEASE,
     335              :                                         100,
     336              :                                         &start,
     337              :                                         &end,
     338              :                                         &progress));
     339            1 :   FAILIF (0 != start);
     340            1 :   return 0;
     341              : }
     342              : 
     343              : 
     344              : /**
     345              :  * The checks to run, in order.
     346              :  */
     347              : static const struct TDB_Test tests[] = {
     348              :   { "work-shards-empty",
     349              :     &check_empty },
     350              :   { "work-shards-begin",
     351              :     &check_begin },
     352              :   { "work-shards-progress",
     353              :     &check_progress },
     354              :   { "work-shards-abort-and-takeover",
     355              :     &check_abort_and_takeover },
     356              :   { "work-shards-delete-locks",
     357              :     &check_delete_locks },
     358              :   { NULL, NULL }
     359              : };
     360              : 
     361              : 
     362              : int
     363            1 : main (int argc,
     364              :       char *const *argv)
     365              : {
     366            1 :   return TDB_main (argc,
     367              :                    argv,
     368              :                    "test-work-shards",
     369              :                    "Tests for the exchangedb `work_shards' table",
     370              :                    tests);
     371              : }
     372              : 
     373              : 
     374              : /* end of test_work_shards.c */
        

Generated by: LCOV version 2.0-1