LCOV - code coverage report
Current view: top level - funk - fd_funk_rec.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 38 38 100.0 %
Date: 2024-11-13 11:58:15 Functions: 28 1452 1.9 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_funk_fd_funk_rec_h
       2             : #define HEADER_fd_src_funk_fd_funk_rec_h
       3             : 
       4             : /* This provides APIs for managing funk records.  It is generally not
       5             :    meant to be included directly.  Use fd_funk.h instead. */
       6             : 
       7             : #include "fd_funk_txn.h" /* Includes fd_funk_base.h */
       8             : 
       9             : /* FD_FUNK_REC_{ALIGN,FOOTPRINT} describe the alignment and footprint of
      10             :    a fd_funk_rec_t.  ALIGN will be an power of 2, footprint will be a
      11             :    multiple of align.  These are provided to facilitate compile time
      12             :    declarations. */
      13             : 
      14             : #define FD_FUNK_REC_ALIGN     (32UL)
      15             : 
      16             : /* FD_FUNK_REC_FLAG_* are flags that can be bit-ored together to specify
      17             :    how records are to be interpreted.
      18             : 
      19             :    - ERASE indicates a record in an in-preparation transaction should be
      20             :    erased if and when the in-preparation transaction is published.  If
      21             :    set, there will be no value resources used by this record.  Will not
      22             :    be set on a published record.  Will not be set if an in-preparation
      23             :    transaction ancestor has this record with erase set.  If set, the
      24             :    first ancestor transaction encountered (going from youngest to
      25             :    oldest) will not have erased set. */
      26             : 
      27   744189564 : #define FD_FUNK_REC_FLAG_ERASE (1UL<<0)
      28             : 
      29             : /* FD_FUNK_REC_IDX_NULL gives the map record idx value used to represent
      30             :    NULL.  This value also set a limit on how large rec_max can be. */
      31             : 
      32  4270200807 : #define FD_FUNK_REC_IDX_NULL (ULONG_MAX)
      33             : 
      34             : /* FD_FUNK_PART_NULL is the partition number of records that are not
      35             :    in a partition */
      36   655138083 : #define FD_FUNK_PART_NULL (UINT_MAX)
      37             : 
      38             : /* A fd_funk_rec_t describes a funk record. */
      39             : 
      40             : struct __attribute__((aligned(FD_FUNK_REC_ALIGN))) fd_funk_rec {
      41             : 
      42             :   /* These fields are managed by the funk's rec_map */
      43             : 
      44             :   fd_funk_xid_key_pair_t pair;     /* Transaction id and record key pair */
      45             :   ulong                  map_next; /* Internal use by map */
      46             :   ulong                  map_hash; /* Internal use by map */
      47             : 
      48             :   /* These fields are managed by funk.  TODO: Consider using record
      49             :      index compression here (much more debatable than in txn itself). */
      50             : 
      51             :   ulong prev_idx;  /* Record map index of previous record */
      52             :   ulong next_idx;  /* Record map index of next record */
      53             :   uint  txn_cidx;  /* Compressed transaction map index (or compressed FD_FUNK_TXN_IDX if this is in the last published) */
      54             :   uint  tag;       /* Internal use only */
      55             :   ulong flags;     /* Flags that indicate how to interpret a record */
      56             : 
      57             :   /* Note: use of uint here requires FD_FUNK_REC_VAL_MAX to be at most
      58             :      UINT_MAX. */
      59             : 
      60             :   uint  val_sz;    /* Num bytes in record value, in [0,val_max] */
      61             :   uint  val_max;   /* Max byte  in record value, in [0,FD_FUNK_REC_VAL_MAX], 0 if erase flag set or val_gaddr is 0 */
      62             :   ulong val_gaddr; /* Wksp gaddr on record value if any, 0 if erase flag set or val_max is 0
      63             :                       If non-zero, the region [val_gaddr,val_gaddr+val_max) will be a current fd_alloc allocation (such that it is
      64             :                       has tag wksp_tag) and the owner of the region will be the record.  IMPORTANT! HAS NO GUARANTEED ALIGNMENT! */
      65             : 
      66             :   ulong prev_part_idx;  /* Record map index of previous record in partition chain */
      67             :   ulong next_part_idx;  /* Record map index of next record in partition chain */
      68             :   uint  part;           /* Partition number, FD_FUNK_PART_NULL if none */
      69             : 
      70             :   int   val_no_free; /* If set, do not call alloc_free on the value */
      71             : 
      72             :   /* Padding to FD_FUNK_REC_ALIGN here (TODO: consider using self index
      73             :      in the structures to accelerate indexing computations if padding
      74             :      permits as this structure is currently has 8 bytes of padding) */
      75             : };
      76             : 
      77             : typedef struct fd_funk_rec fd_funk_rec_t;
      78             : 
      79             : FD_STATIC_ASSERT( sizeof(fd_funk_rec_t) == 5U*32U, record size is wrong );
      80             : 
      81             : /* fd_funk_rec_map allows for indexing records by their (xid,key) pair.
      82             :    It is used to store all records of the last published transaction and
      83             :    the records being updated for a transaction that is in-preparation.
      84             :    Published records are stored under the pair (root,key).  (This is
      85             :    done so that publishing a transaction doesn't require updating all
      86             :    transaction id of all the records that were not updated by the
      87             :    publish.) */
      88             : 
      89             : #define MAP_NAME              fd_funk_rec_map
      90             : #define MAP_T                 fd_funk_rec_t
      91             : #define MAP_KEY_T             fd_funk_xid_key_pair_t
      92             : #define MAP_KEY               pair
      93  1064751135 : #define MAP_KEY_EQ(k0,k1)     fd_funk_xid_key_pair_eq((k0),(k1))
      94   584361765 : #define MAP_KEY_HASH(k0,seed) fd_funk_xid_key_pair_hash((k0),(seed))
      95     6478461 : #define MAP_KEY_COPY(kd,ks)   fd_funk_xid_key_pair_copy((kd),(ks))
      96  9627330816 : #define MAP_NEXT              map_next
      97             : #define MAP_HASH              map_hash
      98             : #define MAP_MAGIC             (0xf173da2ce77ecdb0UL) /* Firedancer rec db version 0 */
      99             : #define MAP_IMPL_STYLE        1
     100             : #define MAP_MEMOIZE           1
     101             : #include "../util/tmpl/fd_map_giant.c"
     102             : 
     103             : FD_PROTOTYPES_BEGIN
     104             : 
     105             : FD_FN_PURE ulong fd_funk_rec_map_list_idx( fd_funk_rec_t const * join, fd_funk_xid_key_pair_t const * key );
     106             : 
     107             : void fd_funk_rec_map_set_key_cnt( fd_funk_rec_t * join, ulong key_cnt );
     108             : 
     109             : /* fd_funk_rec_idx_is_null returns 1 if idx is FD_FUNK_REC_IDX_NULL and
     110             :    0 otherwise. */
     111             : 
     112  4063155303 : FD_FN_CONST static inline int fd_funk_rec_idx_is_null( ulong idx ) { return idx==FD_FUNK_REC_IDX_NULL; }
     113             : 
     114             : /* Accessors */
     115             : 
     116             : /* fd_funk_rec_cnt returns the number of records in the record map.
     117             :    Assumes map==fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) where funk
     118             :    is a current local join.  See fd_funk.h for fd_funk_rec_max. */
     119             : 
     120           3 : FD_FN_PURE static inline ulong fd_funk_rec_cnt( fd_funk_rec_t const * map ) { return fd_funk_rec_map_key_cnt( map ); }
     121             : 
     122             : /* fd_funk_rec_is_full returns 1 if the record map is full (i.e. the
     123             :    maximum of records that can be concurrently tracked has been reached)
     124             :    and 0 otherwise.  Note that this includes all the records in the last
     125             :    published transactions and records being updated by in-preparation
     126             :    transactions.  Assumes funk is a current local join and
     127             :    map==fd_funk_rec_map( funk, fd_funk_wksp( funk ) ). */
     128             : 
     129    52934394 : FD_FN_PURE static inline int fd_funk_rec_is_full( fd_funk_rec_t const * map ) { return fd_funk_rec_map_is_full( map ); }
     130             : 
     131             : /* fd_funk_rec_query queries the in-preparation transaction pointed to
     132             :    by txn for the record whose key matches the key pointed to by key.
     133             :    If txn is NULL, the query will be done for the funk's last published
     134             :    transaction.  Returns a pointer to current record on success and NULL
     135             :    on failure.  Reasons for failure include txn is neither NULL nor a
     136             :    pointer to a in-preparation transaction, key is NULL or not a record
     137             :    in the given transaction.
     138             : 
     139             :    The returned pointer is in the caller's address space and, if the
     140             :    return value is non-NULL, the lifetime of the returned pointer is the
     141             :    lesser of the current local join, the key is removed from the
     142             :    transaction, the lifetime of the in-preparation transaction (txn is
     143             :    non-NULL) or the next successful publication (txn is NULL).
     144             : 
     145             :    Assumes funk is a current local join (NULL returns NULL), txn is NULL
     146             :    or points to an in-preparation transaction in the caller's address
     147             :    space, key points to a record key in the caller's address space (NULL
     148             :    returns NULL), and no concurrent operations on funk, txn or key.
     149             :    funk retains no interest in key.  The funk retains ownership of any
     150             :    returned record.  The record value metadata will be updated whenever
     151             :    the record value modified.
     152             : 
     153             :    These are a reasonably fast O(1).
     154             : 
     155             :    fd_funk_rec_query_global is the same but will query txn's ancestors
     156             :    for key from youngest to oldest if key is not part of txn.  As such,
     157             :    the txn of the returned record may not match txn but will be the txn
     158             :    of most recent ancestor with the key otherwise.
     159             : 
     160             :    fd_funk_rec_query_global_const is the same but it is safe to have
     161             :    multiple threads concurrently run queries.
     162             : 
     163             :    Important safety tip!  These functions can potentially return records
     164             :    that have the ERASE flag set.  (This allows, for example, a caller to
     165             :    discard an erase for an unfrozen in-preparation transaction.)  In
     166             :    such cases, the record will have no value resources in use.
     167             : 
     168             :    These are a reasonably fast O(in_prep_ancestor_cnt). */
     169             : 
     170             : FD_FN_PURE fd_funk_rec_t const *
     171             : fd_funk_rec_query( fd_funk_t *               funk,
     172             :                    fd_funk_txn_t const *     txn,
     173             :                    fd_funk_rec_key_t const * key );
     174             : 
     175             : 
     176             : /* fd_funk_rec_query_global is a query that searches for the
     177             :    supplied key by walking up the funk txn stack.
     178             : 
     179             :    if txn_out is supplied (non-null), the txn the key was found in
     180             :    is returned. If *txn_out == NULL, the key was found in the root
     181             :    context */
     182             : FD_FN_PURE fd_funk_rec_t const *
     183             : fd_funk_rec_query_global( fd_funk_t *               funk,
     184             :                           fd_funk_txn_t const *     txn,
     185             :                           fd_funk_rec_key_t const * key,
     186             :                           fd_funk_txn_t const **    txn_out );
     187             : 
     188             : /* fd_funk_rec_query_safe is a query that is safe in the presence of
     189             :    concurrent writes. The result data is copied into a buffer
     190             :    allocated by the given valloc and should be freed with the same
     191             :    valloc. NULL is returned if the query fails. The query is always
     192             :    against the root transaction. */
     193             : 
     194             : FD_FN_PURE void *
     195             : fd_funk_rec_query_safe( fd_funk_t *               funk,
     196             :                         fd_funk_rec_key_t const * key,
     197             :                         fd_valloc_t               valloc,
     198             :                         ulong *                   result_len );
     199             : 
     200             : FD_FN_PURE void *
     201             : fd_funk_rec_query_xid_safe( fd_funk_t *               funk,
     202             :                             fd_funk_rec_key_t const * key,
     203             :                             fd_funk_txn_xid_t const * xid,
     204             :                             fd_valloc_t               valloc,
     205             :                             ulong *                   result_len );
     206             : 
     207             : /* fd_funk_rec_test tests the record pointed to by rec.  Returns
     208             :    FD_FUNK_SUCCESS (0) if rec appears to be a live unfrozen record in
     209             :    funk and a FD_FUNK_ERR_* (negative) otherwise.  Specifically:
     210             : 
     211             :      FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is clearly
     212             :        not from funk, etc)
     213             : 
     214             :      FD_FUNK_ERR_KEY - the record did not appear to be a live record.
     215             :        Specifically rec's (xid,key) did not resolve to to itself.
     216             : 
     217             :      FD_FUNK_ERR_XID - memory corruption was detected in testing rec
     218             : 
     219             :      FD_FUNK_ERR_FROZEN - rec is part of a frozen transaction
     220             : 
     221             :    If fd_funk_rec_test returns SUCCESS, modify and remove are guaranteed
     222             :    to succeed immediately after return.  The value returned by test will
     223             :    stable for the same lifetime as a modify.
     224             : 
     225             :    Assumes funk is a current local join (NULL returns NULL).
     226             : 
     227             :    This is a reasonably fast O(1). */
     228             : 
     229             : FD_FN_PURE int
     230             : fd_funk_rec_test( fd_funk_t *           funk,
     231             :                   fd_funk_rec_t const * rec );
     232             : 
     233             : /* fd_funk_rec_{pair,xid,key} returns a pointer in the local address
     234             :    space of the {(transaction id,record key) pair,transaction id,record
     235             :    key} of a live record.  Assumes rec points to a live record in the
     236             :    caller's address space.  The lifetime of the returned pointer is the
     237             :    same as rec.  The value at the pointer will be constant for its
     238             :    lifetime. */
     239             : 
     240   303882510 : FD_FN_CONST static inline fd_funk_xid_key_pair_t const * fd_funk_rec_pair( fd_funk_rec_t const * rec ) { return &rec->pair;    }
     241  1631683956 : FD_FN_CONST static inline fd_funk_txn_xid_t const *      fd_funk_rec_xid ( fd_funk_rec_t const * rec ) { return rec->pair.xid; }
     242   803710527 : FD_FN_CONST static inline fd_funk_rec_key_t const *      fd_funk_rec_key ( fd_funk_rec_t const * rec ) { return rec->pair.key; }
     243             : 
     244             : /* fd_funk_rec_txn returns the in-preparation transaction to which the
     245             :    live record rec belongs or NULL if rec belongs to the last published
     246             :    transaction.
     247             : 
     248             :    fd_funk_rec_{next,prev} returns the {next,prev} record
     249             :    ({younger,older} record) of the set of records to which rec belongs
     250             :    or NULL if rec is the {youngest,oldest}.
     251             : 
     252             :    fd_funk_txn_rec_{head,tail} returns the {head,tail} record
     253             :    ({oldest,youngest} record) of the in-preparation transaction to which
     254             :    rec belongs or NULL if rec_next is the {youngest,oldest} in that
     255             :    transaction.
     256             : 
     257             :    All pointers are in the caller's address space.  These are all a fast
     258             :    O(1) but not fortified against memory data corruption. */
     259             : 
     260             : FD_FN_PURE static inline fd_funk_txn_t const *     /* Lifetime as described in fd_funk_txn_query */
     261             : fd_funk_rec_txn( fd_funk_rec_t const * rec,        /* Assumes live funk record, funk current local join */
     262   227968530 :                  fd_funk_txn_t const * txn_map ) { /* Assumes == fd_funk_txn_map( funk, fd_funk_wksp( funk ) ) */
     263   227968530 :   ulong txn_idx = fd_funk_txn_idx( rec->txn_cidx );
     264   227968530 :   if( fd_funk_txn_idx_is_null( txn_idx ) ) return NULL; /* TODO: consider branchless */
     265   120381033 :   return txn_map + txn_idx;
     266   227968530 : }
     267             : 
     268             : FD_FN_PURE static inline fd_funk_rec_t const *      /* Lifetime as described in fd_funk_rec_query */
     269             : fd_funk_rec_next( fd_funk_rec_t const * rec,        /* Assumes live funk record, funk current local join */
     270   439209666 :                   fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
     271   439209666 :   ulong rec_idx = rec->next_idx;
     272   439209666 :   if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL; /* TODO: consider branchless */
     273   359823306 :   return rec_map + rec_idx;
     274   439209666 : }
     275             : 
     276             : FD_FN_PURE static inline fd_funk_rec_t const *      /* Lifetime as described in fd_funk_rec_query */
     277             : fd_funk_rec_prev( fd_funk_rec_t const * rec,        /* Assumes live funk record, funk current local join */
     278   219604833 :                   fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
     279   219604833 :   ulong rec_idx = rec->prev_idx;
     280   219604833 :   if( fd_funk_rec_idx_is_null( rec_idx ) ) return NULL; /* TODO: consider branchless */
     281   179911653 :   return rec_map + rec_idx;
     282   219604833 : }
     283             : 
     284             : FD_FN_PURE static inline fd_funk_rec_t const *          /* Lifetime as described in fd_funk_rec_query */
     285             : fd_funk_txn_rec_head( fd_funk_txn_t const * txn,        /* Assumes an in-preparation transaction, funk current local join */
     286   164233041 :                       fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
     287   164233041 :   ulong rec_head_idx = txn->rec_head_idx;
     288   164233041 :   if( fd_funk_rec_idx_is_null( rec_head_idx ) ) return NULL; /* TODO: consider branchless */
     289   153933318 :   return rec_map + rec_head_idx;
     290   164233041 : }
     291             : 
     292             : FD_FN_PURE static inline fd_funk_rec_t const *          /* Lifetime as described in fd_funk_rec_query */
     293             : fd_funk_txn_rec_tail( fd_funk_txn_t const * txn,        /* Assumes an in-preparation transaction, funk current local join */
     294   117381282 :                       fd_funk_rec_t const * rec_map ) { /* Assumes == fd_funk_rec_map( funk, fd_funk_wksp( funk ) ) */
     295   117381282 :   ulong rec_tail_idx = txn->rec_tail_idx;
     296   117381282 :   if( fd_funk_rec_idx_is_null( rec_tail_idx ) ) return NULL; /* TODO: consider branchless */
     297   117381282 :   return rec_map + rec_tail_idx;
     298   117381282 : }
     299             : 
     300             : /* fd_funk_rec_modify returns rec as a non-const rec if rec is currently
     301             :    safe to modify the val / discard a change to the record for an
     302             :    in-preparation transaction (incl discard an erase) / erase a
     303             :    published record / etc.  Reasons for NULL include NULL funk, NULL
     304             :    rec, rec does not appear to be a live record, or the transaction to
     305             :    which rec belongs is frozen.
     306             : 
     307             :    The returned pointer is in the caller's address space and, if the
     308             :    return value is non-NULL, the lifetime of the returned pointer is the
     309             :    the lesser of the current local join, the key has been removed from
     310             :    the transaction, the transaction becomes frozen due to birth of an
     311             :    in-preparation child, (for a key part of an in-preparation
     312             :    transaction) the lifetime of in-preparation transaction, (for a key
     313             :    part of the last published transaction) the next successful
     314             :    publication.
     315             : 
     316             :    Assumes funk is a current local join (NULL returns NULL), rec is a
     317             :    pointer in the caller's address space to a fd_funk_rec_t (NULL
     318             :    returns NULL), and no concurrent operations on funk or rec.  The funk
     319             :    retains ownership of rec.  The record value metadata will be updated
     320             :    whenever the record value modified.
     321             : 
     322             :    This is a reasonably fast O(1). */
     323             : 
     324             : FD_FN_PURE fd_funk_rec_t *
     325             : fd_funk_rec_modify( fd_funk_t *           funk,
     326             :                     fd_funk_rec_t const * rec );
     327             : 
     328             : /* Returns 1 if the record has been modified in its transaction
     329             :    compared to the prior incarnation of the record with the same
     330             :    key (or there is no prior incarnation). Returns -1 if rec is part
     331             :    of a published transaction. Return 0 otherwise. */
     332             : 
     333             : FD_FN_PURE int
     334             : fd_funk_rec_is_modified( fd_funk_t *           funk,
     335             :                          fd_funk_rec_t const * rec );
     336             : 
     337             : /* TODO: Consider instead doing something like: modify_init, modify_fini and
     338             :    preventing forking the txn if records are being modified instead of
     339             :    the long laundry list of lifetime constraints? */
     340             : 
     341             : /* fd_funk_rec_insert inserts a new record whose key will be the same
     342             :    as the record key pointed to by key to the in-preparation transaction
     343             :    pointed to by txn.  If txn is NULL, the record will be inserted into
     344             :    the last published transaction.
     345             : 
     346             :    Returns a pointer to the new record on success and NULL on failure.
     347             :    If opt_err is non-NULL, on return, *opt_err will indicate the result
     348             :    of the operation.
     349             : 
     350             :      FD_FUNK_SUCCESS - success
     351             : 
     352             :      FD_FUNK_ERR_INVAL - failed due to bad inputs (NULL funk, NULL key,
     353             :        txn was neither NULL nor pointer to an in-preparation
     354             :        transaction)
     355             : 
     356             :      FD_FUNK_ERR_REC - failed due to too many records in the func,
     357             :        increase rec_max
     358             : 
     359             :      FD_FUNK_ERR_FROZEN - txn is a transaction that is a parent of an
     360             :        in-preparation transaction.
     361             : 
     362             :      FD_FUNK_ERR_KEY - key referred to an record that is already present
     363             :        in the transaction.
     364             : 
     365             :    The returned pointer is in the caller's address space and, if the
     366             :    return value is non-NULL, the lifetime of the returned pointer is the
     367             :    lesser of the current local join, the record is removed, the txn's
     368             :    lifetime (only applicable if txn is non-NULL) or the next successful
     369             :    publication (only applicable if txn is NULL).
     370             : 
     371             :    Note, if this insert is for a record that in txn with the ERASE flag
     372             :    set, the ERASE flag of the record will be cleared and it will return
     373             :    that record.
     374             : 
     375             :    Assumes funk is a current local join (NULL returns NULL), txn is NULL
     376             :    or points to an in-preparation transaction in the caller's address
     377             :    space, key points to a record key in the caller's address space (NULL
     378             :    returns NULL), and no concurrent operations on funk, txn or key.
     379             :    funk retains no interest in key or opt_err.  The funk retains
     380             :    ownership of txn and any returned record.  The record value metadata
     381             :    will be updated whenever the record value modified.
     382             : 
     383             :    This is a reasonably fast O(1) and fortified against memory
     384             :    corruption.
     385             : 
     386             :    Note that when a record is newly created, it is initially created
     387             :    with a NULL value.  If intending to modify the value in the most
     388             :    recent ancestor version of the record, a record can be loaded with
     389             :    the data via:
     390             : 
     391             :      fd_funk_val_copy( rec, fd_funk_val_const( orig_rec ), fd_funk_val_sz( orig_rec ), 0UL, alloc, wksp, NULL );
     392             : 
     393             :      // Note: could use fd_funk_val_max( orig_rec ) or some other
     394             :      // intelligence about planned changes via sz_est instead of 0UL
     395             : 
     396             :    This is O(orig_rec) size.  If the caller doesn't have the
     397             :    original record lying around, it can be found via:
     398             : 
     399             :      fd_funk_rec_t const * orig_rec = fd_funk_rec_query_global( funk, txn_parent, key, NULL );
     400             : 
     401             :    This is O(ancestor depth to orig rec) and accounts for that the
     402             :    previous version of the record might not be in txn's parent. */
     403             : 
     404             : fd_funk_rec_t const *
     405             : fd_funk_rec_insert( fd_funk_t *               funk,
     406             :                     fd_funk_txn_t *           txn,
     407             :                     fd_funk_rec_key_t const * key,
     408             :                     int *                     opt_err );
     409             : 
     410             : /* fd_funk_rec_remove removes the live record pointed to by rec from
     411             :    the funk.  Returns FD_FUNK_SUCCESS (0) on success and a FD_FUNK_ERR_*
     412             :    (negative) on failure.  Reasons for failure include:
     413             : 
     414             :      FD_FUNK_ERR_INVAL - bad inputs (NULL funk, NULL rec, rec is
     415             :        obviously not from funk, etc)
     416             : 
     417             :      FD_FUNK_ERR_KEY - the record did not appear to be a live record.
     418             :        Specifically, a record query of funk for rec's (xid,key) pair did
     419             :        not return rec.
     420             : 
     421             :      FD_FUNK_ERR_XID - the record to remove is published but erase was
     422             :        not specified.
     423             : 
     424             :      FD_FUNK_ERR_FROZEN - rec is part of a transaction that is frozen.
     425             : 
     426             :    All changes to the record in that transaction will be undone.
     427             : 
     428             :    Further, if erase is zero, if and when the transaction is published
     429             :    (assuming no subsequent insert of key into that transaction), no
     430             :    changes will be made to the published record.  This type of remove
     431             :    cannot be done on a published record.
     432             : 
     433             :    However, if erase is non-zero, the record will cease to exist in that
     434             :    transaction and any of transaction's subsequently created descendants
     435             :    (again, assuming no subsequent insert of key).  This type of remove
     436             :    can be done on a published record (assuming the last published
     437             :    transaction is unfrozen).
     438             : 
     439             :    Any information in an erased record is lost.
     440             : 
     441             :    Detailed record erasure handling:
     442             : 
     443             :      rec's  | erase | rec's     | rec's | return     | info
     444             :      txn    | req   | txn       | erase |            |
     445             :      frozen |       | published |       |            |
     446             :      -------+-------+-----------+-------+------------+-----
     447             :      no     | no    | no        | clear | SUCCESS    | discards updates to a record, rec dead on return
     448             :      no     | no    | no        | set   | SUCCESS    | discards erase of most recent ancestor, rec dead on return
     449             :      no     | no    | yes       | clear | ERR_XID    | can't revert published record to an older version, rec live on return
     450             :      no     | no    | yes       | set   | *LOG_CRIT* | detected corruption, repurpose to allow for unerasable recs?
     451             :      no     | yes   | no        | clear | SUCCESS    | erase the most recent ancestor version of rec, if no such ancestor version,
     452             :             |       |           |       |            | rec dead on return, otherwise, rec live on return and rec's erase will be
     453             :             |       |           |       |            | set, O(ancestor_hops_to_last_publish) worst case
     454             :      no     | yes   | no        | set   | SUCCESS    | no-op, previously marked erase, rec live on return
     455             :      no     | yes   | yes       | clear | SUCCESS    | erases published record, rec dead on return
     456             :      no     | yes   | yes       | set   | *LOG_CRIT* | detected corruption, repurpose to allow for unerasable recs?
     457             :      yes    | -     | -         | -     | ERR_FROZEN | can't remove rec because rec's txn is frozen, rec live on return
     458             : 
     459             :    On ERR_INVAL and ERR_KEY, rec didn't seem to point to a live record
     460             :    on entry with and still doesn't on return.
     461             : 
     462             :    Assumes funk is a current local join (NULL returns ERR_INVAL) and rec
     463             :    points to a record in the caller's address space (NULL returns
     464             :    ERR_INVAL).  As the funk still has ownership of rec before and after
     465             :    the call if live, the user doesn't need to, for example, match
     466             :    inserts with removes.
     467             : 
     468             :    This is a reasonably fast O(1) except in the case noted above and
     469             :    fortified against memory corruption.
     470             : 
     471             :    IMPORTANT SAFETY TIP!  DO NOT CAST AWAY CONST FROM A FD_FUNK_REC_T TO
     472             :    USE THIS FUNCTION (E.G. PASS A RESULT DIRECTLY FROM QUERY).  USE A
     473             :    LIVE RESULT FROM FD_FUNK_REC_MODIFY! */
     474             : 
     475             : int
     476             : fd_funk_rec_remove( fd_funk_t *     funk,
     477             :                     fd_funk_rec_t * rec,
     478             :                     int             erase );
     479             : 
     480             : /* fd_funk_rec_write_prepare combines several operations into one
     481             :    convenient package. There are 3 basic cases:
     482             : 
     483             :    1. If the given record key already exists in the transaction, the
     484             :    record is returned in modifiable form. This is equivalent to
     485             :    fd_funk_rec_query combined with fd_funk_rec_modify.
     486             : 
     487             :    2. If the record key is entirely new (not present in the
     488             :    transaction or any of its ancestors), a new record is inserted and
     489             :    returned in modifiable form. This is equivalent to
     490             :    fd_funk_rec_insert combined with fd_funk_rec_modify. Note that if
     491             :    the do_create argument is false, a NULL will be returned in this case.
     492             : 
     493             :    3. Otherwise, the record is copied from the ancestor transaction
     494             :    into the given transaction. This is returned in modifiable
     495             :    form. This is equivalent to fd_funk_rec_query_global,
     496             :    fd_funk_rec_insert, fd_funk_val_copy, and fd_funk_rec_modify.
     497             : 
     498             :    In all cases, the record is grown to min_val_size if it is less
     499             :    than this size, padding with zeros if necessary.
     500             : 
     501             :    The irec argument is the previous incarnation of the record if
     502             :    known (i.e. the result of fd_funk_rec_query_global( funk, txn, key
     503             :    ) ). This allows the elimination of the query in some cases. Use
     504             :    NULL if this value is unavailable. */
     505             : fd_funk_rec_t *
     506             : fd_funk_rec_write_prepare( fd_funk_t *               funk,         /* Funky database */
     507             :                            fd_funk_txn_t *           txn,          /* Write the record into this transaction */
     508             :                            fd_funk_rec_key_t const * key,          /* Key of new/modified record */
     509             :                            ulong                     min_val_size, /* Minimum value size of writable record */
     510             :                            int                       do_create,    /* Can create new record */
     511             :                            fd_funk_rec_t const *     irec,         /* Prior result of fd_funk_rec_query_global if known */
     512             :                            int *                     opt_err );    /* Optional error code return */
     513             : 
     514             : /* Misc */
     515             : 
     516             : /* fd_funk_rec_verify verifies the record map.  Returns FD_FUNK_SUCCESS
     517             :    if the record map appears intact and FD_FUNK_ERR_INVAL if not (logs
     518             :    details).  Meant to be called as part of fd_funk_verify.  As such, it
     519             :    assumes funk is non-NULL, fd_funk_{wksp,txn_map,rec_map} have been
     520             :    verified to work and the txn_map has been verified. */
     521             : 
     522             : int
     523             : fd_funk_rec_verify( fd_funk_t * funk );
     524             : 
     525             : FD_PROTOTYPES_END
     526             : 
     527             : #endif /* HEADER_fd_src_funk_fd_funk_rec_h */

Generated by: LCOV version 1.14