code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/volume_discount_stats.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package sqlstore 17 18 import ( 19 "context" 20 "encoding/json" 21 "fmt" 22 "strings" 23 "time" 24 25 "code.vegaprotocol.io/vega/datanode/entities" 26 "code.vegaprotocol.io/vega/datanode/metrics" 27 v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2" 28 "code.vegaprotocol.io/vega/protos/vega" 29 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 30 31 "github.com/georgysavva/scany/pgxscan" 32 ) 33 34 var volumeDiscountStatsOrdering = TableOrdering{ 35 ColumnOrdering{Name: "at_epoch", Sorting: DESC}, 36 ColumnOrdering{Name: "party_id", Sorting: ASC, Ref: "stats->>'party_id'"}, 37 } 38 39 type ( 40 VolumeDiscountStats struct { 41 *ConnectionSource 42 } 43 ) 44 45 func NewVolumeDiscountStats(connectionSource *ConnectionSource) *VolumeDiscountStats { 46 return &VolumeDiscountStats{ 47 ConnectionSource: connectionSource, 48 } 49 } 50 51 func (s *VolumeDiscountStats) Add(ctx context.Context, stats *entities.VolumeDiscountStats) error { 52 defer metrics.StartSQLQuery("VolumeDiscountStats", "Add")() 53 _, err := s.Exec( 54 ctx, 55 `INSERT INTO volume_discount_stats(at_epoch, parties_volume_discount_stats, vega_time) 56 values ($1, $2, $3)`, 57 stats.AtEpoch, 58 stats.PartiesVolumeDiscountStats, 59 stats.VegaTime, 60 ) 61 62 return err 63 } 64 65 func (s *VolumeDiscountStats) LatestStats(ctx context.Context, partyID string) (entities.VolumeDiscountStats, error) { 66 query := `SELECT * FROM volume_discount_stats WHERE 67 at_epoch = (SELECT id - 1 from current_epochs ORDER BY id DESC, vega_time DESC FETCH FIRST ROW ONLY) AND 68 EXISTS (SELECT TRUE FROM jsonb_array_elements(parties_volume_discount_stats) ps WHERE ps->>'party_id' = '$1')` 69 ent := []entities.VolumeDiscountStats{} 70 if err := pgxscan.Select(ctx, s.ConnectionSource, &ent, query, partyID); err != nil { 71 return entities.VolumeDiscountStats{}, err 72 } 73 if len(ent) == 0 { 74 return entities.VolumeDiscountStats{}, nil 75 } 76 final := ent[0] 77 stats := make([]*eventspb.PartyVolumeDiscountStats, 0, len(ent)) 78 for _, e := range ent { 79 for _, stat := range e.PartiesVolumeDiscountStats { 80 if stat.PartyId == partyID { 81 stats = append(stats, stat) 82 } 83 } 84 } 85 // running volume and discount factor to be used to match the volume discount program 86 final.PartiesVolumeDiscountStats = stats 87 return final, nil 88 } 89 90 func (s *VolumeDiscountStats) Stats(ctx context.Context, atEpoch *uint64, partyID *string, pagination entities.CursorPagination) ([]entities.FlattenVolumeDiscountStats, entities.PageInfo, error) { 91 defer metrics.StartSQLQuery("VolumeDiscountStats", "VolumeDiscountStats")() 92 93 var ( 94 args []any 95 pageInfo entities.PageInfo 96 ) 97 98 filters := []string{} 99 filters = append(filters, "jsonb_typeof(parties_volume_discount_stats) != 'null'") 100 101 if atEpoch != nil { 102 filters = append(filters, fmt.Sprintf("at_epoch = %s", nextBindVar(&args, atEpoch))) 103 } 104 if partyID != nil { 105 filters = append(filters, fmt.Sprintf("stats->>'party_id' = %s", nextBindVar(&args, partyID))) 106 } 107 108 if partyID == nil && atEpoch == nil { 109 filters = append(filters, "at_epoch = (SELECT MAX(at_epoch) FROM volume_discount_stats)") 110 } 111 112 stats := []struct { 113 AtEpoch uint64 114 PartyID string 115 RunningVolume string 116 DiscountFactors string 117 VegaTime time.Time 118 }{} 119 query := `select at_epoch, stats->>'party_id' as party_id, stats->>'running_volume' as running_volume, stats->>'discount_factors' as discount_factors, vega_time from volume_discount_stats, jsonb_array_elements(parties_volume_discount_stats) AS stats` 120 121 if len(filters) > 0 { 122 query = fmt.Sprintf("%s where %s", query, strings.Join(filters, " and ")) 123 } 124 125 query, args, err := PaginateQuery[entities.VolumeDiscountStatsCursor](query, args, volumeDiscountStatsOrdering, pagination) 126 if err != nil { 127 return nil, pageInfo, err 128 } 129 130 if err := pgxscan.Select(ctx, s.ConnectionSource, &stats, query, args...); err != nil { 131 return nil, pageInfo, err 132 } 133 134 flattenStats := []entities.FlattenVolumeDiscountStats{} 135 for _, stat := range stats { 136 discountFactors := &vega.DiscountFactors{} 137 if err := json.Unmarshal([]byte(stat.DiscountFactors), discountFactors); err != nil { 138 return nil, pageInfo, err 139 } 140 141 flattenStats = append(flattenStats, entities.FlattenVolumeDiscountStats{ 142 AtEpoch: stat.AtEpoch, 143 PartyID: stat.PartyID, 144 DiscountFactors: discountFactors, 145 RunningVolume: stat.RunningVolume, 146 VegaTime: stat.VegaTime, 147 }) 148 } 149 150 flattenStats, pageInfo = entities.PageEntities[*v2.VolumeDiscountStatsEdge](flattenStats, pagination) 151 152 return flattenStats, pageInfo, nil 153 }