Advertisement
chadjoan

Draft v0 Kickback SQL transactions and orders

Apr 11th, 2024 (edited)
855
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 4.83 KB | None | 0 0
  1. <?MySQL
  2. CREATE TABLE transaction_header
  3. (
  4.     id_time  DATETIME NOT NULL,
  5.     id_rand  BIGINT   NOT NULL,
  6.     status   SMALLINT NOT NULL, # Ex: processing, aborted, committed (TODO: Make a table enumerating the possible values; use foreign key to constrain this field to valid possibilities.)
  7.    PRIMARY  KEY (id_time, id_rand)
  8. );
  9.  
  10. CREATE TABLE transaction_details
  11. (
  12.     id_time   DATETIME NOT NULL,
  13.     id_rand   BIGINT   NOT NULL,
  14.  
  15.     # TODO: These might need to be specified differently.
  16.    # Ideally they'd reference rows in MySQL schema's table and column tables.
  17.    # (So far I don't see any keys on MySQL schema's table table, so we might
  18.    # have to do something more complicated to make table references... :/ )
  19.    table_id  INTEGER  NOT NULL,
  20.     column_id INTEGER  NOT NULL,
  21.     ...
  22.     PRIMARY  KEY (id_time, id_rand, table_id, column_id),
  23.     INDEX    index_header (id_time, id_rand),
  24.     FOREIGN  KEY (index_header) REFERENCES transaction_header (id_time, id_rand)
  25. );
  26.  
  27. CREATE TABLE store
  28. (
  29.     store_ctime         DATETIME NOT NULL,
  30.     store_crand         BIGINT   NOT NULL,
  31.     name                VARCHAR(128),
  32.     ...
  33.     PRIMARY KEY (store_ctime, store_crand),
  34.     FOREIGN KEY (PRIMARY) REFERENCES transaction_header (id_time, id_rand)
  35. );
  36.  
  37. CREATE TABLE order_header
  38. (
  39.     order_ctime      DATETIME NOT NULL,
  40.     order_crand      BIGINT   NOT NULL,
  41.     ref_store_ctime  DATETIME NOT NULL,
  42.     ref_store_crand  BIGINT   NOT NULL,
  43.     ...
  44.  
  45.     PRIMARY KEY (order_ctime, order_crand),
  46.     INDEX   index_ref_store (ref_store_ctime, ref_store_crand),
  47.     FOREIGN KEY (PRIMARY) REFERENCES transaction_header (id_time, id_rand),
  48.     FOREIGN KEY (index_ref_store) REFERENCES store (store_ctime, store_crand)
  49. );
  50. ?>
  51.  
  52.  
  53.  
  54.  
  55. <?php
  56. Insertion logic:
  57.  
  58. // Once you have acquired a transaction ID, you can modify the values
  59. function acquire_transaction_id(MySQLi $conn : &$id_time, &$id_rand) : bool
  60. {
  61.     $salt = ...; // Hash of MAC address xor/plus/minus time of thread start, or something like that.
  62.  
  63.     assert(sizeof(int) === 8);
  64.     $id_time = get_cpu_hnsecs(); // 64-bit int
  65.     $seed = $id_time + $salt;
  66.     $id_rand = get_random_int32($seed);
  67.     $id_time_str = get_utc_datetime_iso8601_etcetc($id_time);
  68.     // NOTE: Alternative design possibility: just store the 64-bit $id_time
  69.     // value without converting it to a date-time. This reduces the chance of
  70.     // calindrical calculations introducing bizarre errors into the system,
  71.     // as dates might not always be monotonically-increasing (!!).
  72.  
  73.     // Assuming you have a $conn variable that represents your database connection
  74.     $stmt = $conn->prepare(
  75.         "INSERT INTO transaction
  76.        (id_time, id_rand)
  77.        VALUES (?, ?)"
  78.     );
  79.  
  80.     if (false === $stmt) {
  81.         error_log($conn->error);
  82.         return false;
  83.     }
  84.  
  85.     // Bind the variables to the SQL statement
  86.     $success = $stmt->bind_param("si", $id_time_str, $id_rand);
  87.  
  88.     if (false === $success) {
  89.         error_log($stmt->error);
  90.         $stmt->close();
  91.         return false;
  92.     }
  93.  
  94.     // Execute the prepared statement
  95.     //
  96.     // In the (already unlikely) event that two transactions are started at the
  97.     // exact same hectonanosecond, then the chances of them colliding will be
  98.     // 1/2^32.
  99.     //
  100.     // So even in the (two-way) worst-case scenario, this loop has a (2^32 - 1)/(2^32)
  101.     // chance of completing successfully on the first iteration.
  102.     //
  103.     // From the perspective of Birthday Problem analysis:
  104.     // 0.5+sqrt(0.25 + 2*ln(2)*2^32) = 77163.24... simultaneous transactions
  105.     // (same hnsec) would be needed to have a 50% chance of causing a collision.
  106.     //
  107.     // So if about 77163 threads all pick the same hectonanosecond to transact
  108.     // upon, there is a 50% chance that this loop will have to ... *gasp* ...
  109.     // execute more than once!
  110.     //
  111.     $count = 0;
  112.     while (true) {
  113.         $success = $stmt->execute();
  114.         if (false !== $success) { // PHP way of checking for truthiness, lol.
  115.             break;
  116.         }
  117.  
  118.         // TODO: Verify that MYSQL error code 1062 is guaranteed to be the "duplicate key" error.
  119.         // (If not, find some way to differentiate between duplication errors and other errors.)
  120.         if ($stmt->errno !== 1062) {
  121.             error_log($stmt->error);
  122.             $stmt->close();
  123.             return false;
  124.         }
  125.  
  126.         $count++;
  127.  
  128.         $seed += $salt; // Make sure this thread and other thread aren't using same seed!
  129.         $id_rand = get_random_int32($seed);
  130.         if ( $count < 5 ) {
  131.             continue;
  132.         }
  133.  
  134.         if ( $count < 50 ) {
  135.             $id_time++;
  136.         } else {
  137.             $id_time = get_cpu_hnsecs();
  138.         }
  139.         $id_time_str = get_utc_datetime_iso8601_etcetc($id_time);
  140.         continue;
  141.     }
  142.     return true;
  143. }
  144.  
  145. ?>
  146.  
  147.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement