-
Notifications
You must be signed in to change notification settings - Fork 499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
/operation_fee_stats endpoint #586
Conversation
I need to check this with real data (I'm on mobile tethering so will do it once on a stable wifi) but doesn't a query like: select
min(fee_paid/operation_count),
mode() within group (order by fee_paid/operation_count)
from history_transactions
where ledger_sequence > {current - x}; return the same data without doing it all manually? Also because this won't change between ledgers we can keep the results in a cache for ~5 seconds so consecutive requests do not recalculate it over and over again. |
As per discussion with @bartekn , next steps are:
Will update PR with this by Saturday night. Today and Friday I will be at RustConf |
@bartekn switched to cache like we described. I made a few design decisions in this process:
|
Also, tests do pass, but I think travis won't since I didn't add any of the generated test files beyond the scc recipes |
type OperationFeeStatsAction struct { | ||
Action | ||
Records []history.Transaction | ||
Ledgers history.LedgerCache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Records
and Ledgers
not used.
services/horizon/internal/app.go
Outdated
|
||
err = a.HistoryQ(). | ||
TransactionsForLastXLedgers(latest.Sequence). | ||
Select(&resp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move Select
inside TransactionsForLastXLedgers
and change it to something like:
TransactionsForLastXLedgers(currentSeq int32, min, max *null.Int) error
Then FeeStats
is not needed anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I am mistaken (still not a golang pro yet), I believe I need some sort of intermediary data structure to capture the response from the sql query -- hence FeeStats
and LatestLedger
, both of which I moved to github.com/stellar/go/services/horizon/internal/db2/history
.
I could accept and min
and mode
as input params and write to them as part of this method, however I would still need something like FeeStats
right?
services/horizon/internal/app.go
Outdated
|
||
if len(resp) < 1 { | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This query will always return at least 1 row.
services/horizon/internal/app.go
Outdated
type LatestLedger struct { | ||
BaseFee int32 `db:"base_fee"` | ||
Sequence int32 `db:"sequence"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move this to github.com/stellar/go/services/horizon/internal/db2/history
package.
Select("min(fee_paid/operation_count), mode() within group (order by fee_paid/operation_count)"). | ||
From("history_transactions"). | ||
Where("ledger_sequence > ?", currentSeq-5), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not GetRaw
as in LatestLedgerBaseFeeAndSequence
? This looks complicated.
err = a.HistoryQ().LatestLedgerBaseFeeAndSequence(&latest) | ||
if err != nil { | ||
goto Failed | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inserted op fee cache update into current horizon tick, I can easily make this every 5 ticks or create a separate tick that goes every 5 sec (my reasoning here was that ledgers too should only be updated every 5 sec, but the update lives within the 1 sec tick)
We can do it even better. We can leave the tick to be every second but then we can check if there is a new ledger (by comparing the result of LatestLedgerBaseFeeAndSequence
and current state in operationfeestats
). If there is a new ledger we can continue calculations, if not we can return early.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call.
@bartekn updated for latest review. One part I have questions about (responded in the comment thread above) is where you said Then FeeStats is not needed anymore. |
services/horizon/internal/app.go
Outdated
var next operationfeestats.State | ||
|
||
var latest history.LatestLedger | ||
var resp history.FeeStats |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename resp
to feeStats
? Easy to mistake.
@@ -16,6 +16,17 @@ func (q *Q) TransactionByHash(dest interface{}, hash string) error { | |||
return q.Get(dest, sql) | |||
} | |||
|
|||
// TransactionsForLastXLedgers filters the query to only the last X ledgers worth of transactions. | |||
// Currently, we hard code the query to return the last 5 ledgers worth of transactions. In the | |||
// future this mya be configurable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mya
-> may
func SetState(next State) { | ||
lock.Lock() | ||
current = next | ||
lock.Unlock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need one more thing here to prevent sync issues (like in #603 but probably less dangerous). UpdateOperationFeeStatsState
is called every second so if one of the queries inside take a long time, the old state can overwrite the newer one:
Time | Tick1 | Tick2 |
---|---|---|
1 | UpdateOperationFeeStatsState start |
|
2 | UpdateOperationFeeStatsState start |
|
3 | Save result: operationfeestats.SetState |
|
4 | Save result: operationfeestats.SetState |
To prevent this we should:
- Check if the
next.LastLedger > current.LastLedger
inSetState
above. - Change
TransactionsForLastXLedgers
to something likeTransactionFeesBetweenLedgers(first int32, last int32, dest interface{})
. So we will always check the correct transactions even if one or more new ledgers appeared in a meantime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Can you add test schema files?
services/horizon/internal/test/t.go
Outdated
func (t *T) UpdateOperationFeeStatsState() { | ||
var err error | ||
var next operationfeestats.State | ||
var latest history.LatestLedger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All Update*
methods that are duplicated from App
should be removed eventually. For now, let's just update them (ex. history db min/mode query is wrong).
Looks like a I have a couple things to fix -- some tests fail and a sort of cyclical import (no sure where that came from). Just rebased and will see if that fixes anything. |
Looks like recent offer liabilities changes broke scc test scenarios |
Tests pass, but needs one more round of cleanup/refactor. |
@bartekn ok so test schema included and everything passes. Only questionable thing left I know of is a |
Yeah 🍾 |
This a rough first draft for this endpoint. Below I have noted the things I am looking for feedback on.
Things that need improvement:
Things up for discussion:
latest_ledger_base_fee
: as per @stanford-scsmode
: the most occurring, accepted transaction over the past five ledgersmin
: the smallest tx fee accepted transaction over the past five ledgersThe test schema build script generated a lot of files I have not included in this PR.
Closes: #516