Use count estimation on miner_info

This commit is contained in:
DataHoarder 2023-07-26 15:59:14 +02:00
parent 3ce0b7d2db
commit 241c4c9263
Signed by: DataHoarder
SSH key fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
6 changed files with 81 additions and 21 deletions

View file

@ -403,28 +403,45 @@ func main() {
return
}
var foundBlocksData [index.InclusionCount]cmdutils.MinerInfoBlockData
if !params.Has("noShares") {
_ = indexDb.Query("SELECT COUNT(*) FILTER (WHERE uncle_of IS NULL) AS share_count, COUNT(*) FILTER (WHERE uncle_of IS NOT NULL) AS uncle_count, coalesce(MAX(side_height), 0) AS last_height, inclusion FROM side_blocks WHERE side_blocks.miner = $1 GROUP BY side_blocks.inclusion ORDER BY inclusion ASC;", func(row index.RowScanInterface) error {
var d cmdutils.MinerInfoBlockData
var inclusion index.BlockInclusion
if err := row.Scan(&d.ShareCount, &d.UncleCount, &d.LastShareHeight, &inclusion); err != nil {
return err
}
if inclusion < index.InclusionCount {
foundBlocksData[inclusion] = d
}
return nil
}, miner.Id())
}
var lastShareHeight uint64
var lastShareTimestamp uint64
if foundBlocksData[index.InclusionInVerifiedChain].ShareCount > 0 && foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight > lastShareHeight {
var foundBlocksData [index.InclusionCount]cmdutils.MinerInfoBlockData
if !params.Has("noShares") {
if params.Has("shareEstimates") {
// estimate counts
_ = indexDb.Query(fmt.Sprintf("SELECT count_estimate('SELECT 1 FROM side_blocks WHERE side_blocks.miner = %d AND uncle_of IS NULL AND inclusion = %d;') AS shares_estimate, count_estimate('SELECT 1 FROM side_blocks WHERE side_blocks.miner = %d AND uncle_of IS NOT NULL AND inclusion = %d;') uncles_estimate, side_height AS last_height, timestamp AS last_timestamp FROM side_blocks WHERE side_blocks.miner = %d AND inclusion = %d ORDER BY side_height DESC LIMIT 1;", miner.Id(), index.InclusionInVerifiedChain, miner.Id(), index.InclusionInVerifiedChain, miner.Id(), index.InclusionInVerifiedChain), func(row index.RowScanInterface) error {
var shareEstimate, uncleEstimate, lastHeight, lastTimestamp uint64
if err := row.Scan(&shareEstimate, &uncleEstimate, &lastHeight, &lastTimestamp); err != nil {
return err
}
foundBlocksData[index.InclusionInVerifiedChain].ShareCount = shareEstimate
foundBlocksData[index.InclusionInVerifiedChain].UncleCount = uncleEstimate
foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight = lastHeight
lastShareHeight = lastHeight
lastShareTimestamp = lastTimestamp
return nil
})
} else {
_ = indexDb.Query("SELECT COUNT(*) FILTER (WHERE uncle_of IS NULL) AS share_count, COUNT(*) FILTER (WHERE uncle_of IS NOT NULL) AS uncle_count, coalesce(MAX(side_height), 0) AS last_height, inclusion FROM side_blocks WHERE side_blocks.miner = $1 GROUP BY side_blocks.inclusion ORDER BY inclusion ASC;", func(row index.RowScanInterface) error {
var d cmdutils.MinerInfoBlockData
var inclusion index.BlockInclusion
if err := row.Scan(&d.ShareCount, &d.UncleCount, &d.LastShareHeight, &inclusion); err != nil {
return err
}
if inclusion < index.InclusionCount {
foundBlocksData[inclusion] = d
}
return nil
}, miner.Id())
}
}
if lastShareHeight == 0 && foundBlocksData[index.InclusionInVerifiedChain].ShareCount > 0 && foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight > lastShareHeight {
lastShareHeight = foundBlocksData[index.InclusionInVerifiedChain].LastShareHeight
}
if lastShareHeight > 0 {
if lastShareTimestamp == 0 && lastShareHeight > 0 {
lastShareTimestamp = indexDb.GetTipSideBlockByHeight(lastShareHeight).Timestamp
}

View file

@ -0,0 +1,12 @@
CREATE OR REPLACE FUNCTION count_estimate(query text) RETURNS integer AS $$
DECLARE
rec record;
rows integer;
BEGIN
FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
rows := substring(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
EXIT WHEN rows IS NOT NULL;
END LOOP;
RETURN rows;
END;
$$ LANGUAGE plpgsql VOLATILE STRICT;

View file

@ -3,7 +3,7 @@ package index
import (
"context"
"database/sql"
_ "embed"
"embed"
"errors"
"fmt"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/address"
@ -15,7 +15,9 @@ import (
"git.gammaspectra.live/P2Pool/p2pool-observer/utils"
"github.com/floatdrop/lru"
"github.com/lib/pq"
"io"
"log"
"path"
"reflect"
"regexp"
"slices"
@ -58,6 +60,9 @@ type Index struct {
//go:embed schema.sql
var dbSchema string
//go:embed functions/*.sql
var dbFunctions embed.FS
func OpenIndex(connStr string, consensus *sidechain.Consensus, difficultyByHeight block.GetDifficultyByHeightFunc, getSeedByHeight block.GetSeedByHeightFunc, getByTemplateId sidechain.GetByTemplateIdFunc) (index *Index, err error) {
index = &Index{
consensus: consensus,
@ -159,6 +164,31 @@ func OpenIndex(connStr string, consensus *sidechain.Consensus, difficultyByHeigh
return nil, err
}
}
if dir, err := dbFunctions.ReadDir("functions"); err != nil {
return nil, err
} else {
for _, e := range dir {
f, err := dbFunctions.Open(path.Join("functions", e.Name()))
if err != nil {
return nil, err
}
var data []byte
func() {
defer f.Close()
data, err = io.ReadAll(f)
}()
if err != nil {
return nil, err
}
if _, err := tx.Exec(string(data)); err != nil {
return nil, err
}
}
}
if err := tx.Commit(); err != nil {
return nil, err
}

View file

@ -52,6 +52,7 @@ curl --silent https://{%s p.Context().NetServiceAddress %}/api/pool_info
<div>
<h3 class="mono">/api/miner_info/&lt;id|address&gt;</h3>
<p>Response contains general information on a specific miner, referred by its internal <em>id</em> or known Monero <em>address</em>.</p>
<p>If you do not need the precise count of shares please pass the <em>?shareEstimates</em> parameter.</p>
<pre class="smaller">
curl --silent https://{%s p.Context().NetServiceAddress %}/api/miner_info/47ab14EokGgCTX7RYoVhrNMjVA7GfW1jyMAmL7qBQz9fa4RZ6ZsBUgeRGuPWjqeM1wLptSJH5xuX2H4mAepMYvu6JqWMsGw
{

View file

@ -146,14 +146,14 @@ type MinerPage struct {
</tr>
<tr><td colspan="5">&nbsp;</td></tr>
<tr>
<th>Total Shares</th>
<th>Estimated Total Shares</th>
<th>Day Shares</th>
<th>Day Hashrate</th>
<th>Day Share %</th>
<th>Estimated Daily Reward</th>
</tr>
<tr>
<td>{% if p.LastPoolBlock != nil %}{%dul p.Miner.Shares[1].ShareCount %} blocks (+{%dul p.Miner.Shares[1].UncleCount %} uncles{% if p.Miner.Shares[0].ShareCount > 0 %}, +{%dul p.Miner.Shares[0].ShareCount %} orphans{% endif %}){% else %}No shares reported{% endif %}</td>
<td>{% if p.LastPoolBlock != nil %}Around {%dul p.Miner.Shares[1].ShareCount %} blocks (+{%dul p.Miner.Shares[1].UncleCount %} uncles{% if p.Miner.Shares[0].ShareCount > 0 %}, +{%dul p.Miner.Shares[0].ShareCount %} orphans{% endif %}){% else %}No shares reported{% endif %}</td>
<td>{%dul p.Positions.Blocks.Total() %} blocks (+{%dul p.Positions.Uncles.Total() %} uncles)</td>
{% code weightRatio := (float64(p.Weight) / (float64(p.Context().Pool.SideChain.Window.Weight.Mul64(4).Lo) * (float64(p.Context().Pool.SideChain.Consensus.ChainWindowSize) / float64(p.Context().Pool.SideChain.Window.Blocks)))) %}
<td>{%s si_units(weightRatio * float64(diff_hashrate(p.Context().Pool.SideChain.LastBlock.Difficulty, p.Context().Consensus.TargetBlockTime)), 3) %}H/s</td>

View file

@ -844,7 +844,7 @@ func main() {
refresh = 300
}
address := mux.Vars(request)["miner"]
miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s", address))
miner := getTypeFromAPI[cmdutils.MinerInfoResult](fmt.Sprintf("miner_info/%s?shareEstimates", address))
if miner == nil || miner.Address == nil {
if addr := address2.FromBase58(address); addr != nil {
miner = &cmdutils.MinerInfoResult{