github.com/decred/dcrlnd@v0.7.6/channeldb/payments_stats.go (about)

     1  package channeldb
     2  
     3  import (
     4  	"bytes"
     5  
     6  	"github.com/decred/dcrlnd/kvdb"
     7  )
     8  
     9  // PaymentCountStats returns stats about the payments stored in the DB.
    10  type PaymentCountStats struct {
    11  	Total     uint64
    12  	Failed    uint64
    13  	Succeeded uint64
    14  
    15  	HTLCAttempts uint64
    16  	HTLCFailed   uint64
    17  	HTLCSettled  uint64
    18  
    19  	OldDupePayments uint64
    20  }
    21  
    22  // CalcPaymentStats goes through the DB, counting up the payment stats.
    23  func (d *DB) CalcPaymentStats() (*PaymentCountStats, error) {
    24  	res := &PaymentCountStats{}
    25  	err := kvdb.View(d, func(tx kvdb.RTx) error {
    26  		paymentsBucket := tx.ReadBucket(paymentsRootBucket)
    27  		if paymentsBucket == nil {
    28  			return nil
    29  		}
    30  
    31  		// Standard payments.
    32  		return paymentsBucket.ForEach(func(k, v []byte) error {
    33  			payBucket := paymentsBucket.NestedReadBucket(k)
    34  			if payBucket == nil {
    35  				return nil
    36  			}
    37  
    38  			res.Total += 1
    39  
    40  			htlcsBucket := payBucket.NestedReadBucket(paymentHtlcsBucket)
    41  			if htlcsBucket != nil {
    42  				var inflight, settled bool
    43  				err := htlcsBucket.ForEach(func(k, _ []byte) error {
    44  					if !bytes.HasPrefix(k, htlcAttemptInfoKey) {
    45  						return nil
    46  					}
    47  					ai := k[len(htlcAttemptInfoKey):]
    48  					res.HTLCAttempts += 1
    49  					hasFail := htlcsBucket.Get(htlcBucketKey(htlcFailInfoKey, ai)) != nil
    50  					hasSettle := htlcsBucket.Get(htlcBucketKey(htlcSettleInfoKey, ai)) != nil
    51  					if hasFail {
    52  						res.HTLCFailed += 1
    53  					} else if hasSettle {
    54  						res.HTLCSettled += 1
    55  						settled = true
    56  					} else {
    57  						inflight = true
    58  					}
    59  					return nil
    60  				})
    61  				if err != nil {
    62  					return err
    63  				}
    64  
    65  				hasFailureReason := payBucket.Get(paymentFailInfoKey) != nil
    66  
    67  				switch {
    68  				// If any of the the HTLCs did succeed and there are no HTLCs in
    69  				// flight, the payment succeeded.
    70  				case !inflight && settled:
    71  					res.Succeeded += 1
    72  
    73  				// If we have no in-flight HTLCs, and the payment failure is set, the
    74  				// payment is considered failed.
    75  				case !inflight && hasFailureReason:
    76  					res.Failed += 1
    77  				}
    78  
    79  			}
    80  
    81  			// Old duplicate payments.
    82  			dupBucket := payBucket.NestedReadBucket(duplicatePaymentsBucket)
    83  			if dupBucket == nil {
    84  				return nil
    85  			}
    86  
    87  			return dupBucket.ForEach(func(k, v []byte) error {
    88  				subBucket := dupBucket.NestedReadBucket(k)
    89  				if subBucket == nil {
    90  					return nil
    91  				}
    92  				res.OldDupePayments += 1
    93  				return nil
    94  			})
    95  		})
    96  	}, func() {})
    97  
    98  	return res, err
    99  }