8000 count addresses endpionts by tdroxler · Pull Request #530 · alephium/explorer-backend · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

count addresses endpionts #530

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ explorer {
cache-block-times-reload-period = 5 seconds
cache-latest-blocks-reload-period = 5 seconds

# Cache reloading intervals for TransactionCache
cache-tx-count-reload-period = 5 seconds
cache-address-count-reload-period = 1 minutes

#How many transaction can be exported at max
export-txs-number-threshold = 10000

Expand Down
174 changes: 174 additions & 0 deletions app/src/main/resources/explorer-backend-openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -5147,6 +5147,180 @@
}
}
},
"/infos/total-addresses": {
"get": {
"tags": [
"Infos"
],
"description": "Get the total number of addresses",
"operationId": "getInfosTotal-addresses",
"responses": {
"200": {
"content": {
"text/plain": {
"schema": {
"type": "integer",
"format": "int32"
}
}
}
},
"400": {
"description": "BadRequest",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequest"
},
"example": {
"detail": "Something bad in the request"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Unauthorized"
},
"example": {
"detail": "You shall not pass"
}
}
}
},
"404": {
"description": "NotFound",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NotFound"
},
"example": {
"resource": "wallet-name",
"detail": "wallet-name not found"
}
}
}
},
"500": {
"description": "InternalServerError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InternalServerError"
},
"example": {
"detail": "Ouch"
}
}
}
},
"503": {
"description": "ServiceUnavailable",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailable"
},
"example": {
"detail": "Self clique unsynced"
}
}
}
}
}
}
},
"/infos/total-holders-estimation": {
"get": {
"tags": [
"Infos"
],
"description": "Estimate the total number of holders (with balance > 0)",
"operationId": "getInfosTotal-holders-estimation",
"responses": {
"200": {
"content": {
"text/plain": {
"schema": {
"type": "integer",
"format": "int32"
}
}
}
},
"400": {
"description": "BadRequest",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BadRequest"
},
"example": {
"detail": "Something bad in the request"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Unauthorized"
},
"example": {
"detail": "You shall not pass"
}
}
}
},
"404": {
"description": "NotFound",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NotFound"
},
"example": {
"resource": "wallet-name",
"detail": "wallet-name not found"
}
}
}
},
"500": {
"description": "InternalServerError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InternalServerError"
},
"example": {
"detail": "Ouch"
}
}
}
},
"503": {
"description": "ServiceUnavailable",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailable"
},
"example": {
"detail": "Self clique unsynced"
}
}
}
}
}
}
},
"/infos/average-block-times": {
"get": {
"tags": [
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/scala/org/alephium/explorer/ExplorerState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ sealed trait ExplorerState extends Service with StrictLogging {
)(groupSettings, executionContext, database.databaseConfig)

lazy val transactionCache: TransactionCache =
TransactionCache(database)(executionContext)
TransactionCache(
database,
config.cacheTxCo 9E7A untReloadPeriod,
config.cacheAddressCountReloadPeriod
)(executionContext)

implicit lazy val blockFlowClient: BlockFlowClient =
BlockFlowClient(
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/scala/org/alephium/explorer/api/InfosEndpoints.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ trait InfosEndpoints extends BaseEndpoint with QueryParams {
.out(plainBody[Int])
.description("Get the total number of transactions")

val getTotalAddresses: BaseEndpoint[Unit, Int] =
infosEndpoint.get
.in("total-addresses")
.out(plainBody[Int])
.description("Get the total number of addresses")

val estimateTotalHolders: BaseEndpoint[Unit, Int] =
infosEndpoint.get
.in("total-holders-estimation")
.out(plainBody[Int])
.description("Estimate the total number of holders (with balance > 0)")

val getAverageBlockTime: BaseEndpoint[Unit, ArraySeq[PerChainDuration]] =
infosEndpoint.get
.in("average-block-times")
Expand Down
21 changes: 21 additions & 0 deletions app/src/main/scala/org/alephium/explorer/api/QueryParams.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,27 @@ trait QueryParams extends TapirCodecs {
)
.validate(TimeInterval.validator)

val optionalTimeIntervalQuery: EndpointInput[Option[TimeInterval]] =
query[Option[TimeStamp]]("fromTs")
.and(query[Option[TimeStamp]]("toTs"))
.map { case (fromOpt, to) =>
fromOpt.map { from =>
TimeInterval(from, to)
}
}(timeInterval => (timeInterval.map(_.from), timeInterval.flatMap(_.toOpt)))
.validate(optTimeIntervalvalidator)

val optTimeIntervalvalidator: Validator[Option[TimeInterval]] = Validator.custom {
case Some(timeInterval) =>
if (timeInterval.from >= timeInterval.to) {
ValidationResult.Invalid(s"`fromTs` must be before `toTs`")
} else {
ValidationResult.Valid
}
case None =>
ValidationResult.Valid
}

def timeIntervalWithMaxQuery(duration: Duration): EndpointInput[TimeInterval] =
timeIntervalQuery
.validate(Validator.custom { timeInterval =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,67 @@ import slick.jdbc.PostgresProfile.api._

import org.alephium.explorer.persistence.Database
import org.alephium.explorer.persistence.DBRunner._
import org.alephium.explorer.persistence.queries.TransactionQueries
import org.alephium.explorer.persistence.queries.{OutputQueries, TransactionQueries}
import org.alephium.util.Service

object TransactionCache {

@SuppressWarnings(Array("org.wartremover.warts.DefaultArguments"))
def apply(database: Database, reloadAfter: FiniteDuration = 5.seconds)(implicit
def apply(
database: Database,
cacheTxCountReloadPeriod: FiniteDuration = 5.seconds,
cacheAddressCountReloadPeriod: FiniteDuration = 5.minutes
)(implicit
ec: ExecutionContext
): TransactionCache =
new TransactionCache(database, reloadAfter)
new TransactionCache(database, cacheTxCountReloadPeriod, cacheAddressCountReloadPeriod)
}

/** Transaction related cache
*
* @param mainChainTxnCount
* Stores current total number of `main_chain` transaction.
*/
class TransactionCache(database: Database, reloadAfter: FiniteDuration)(implicit
class TransactionCache(
database: Database,
cacheTxCountReloadPeriod: FiniteDuration,
cacheAddressCountReloadPeriod: FiniteDuration
)(implicit
val executionContext: ExecutionContext
) extends Service {

private val mainChainTxnCount: AsyncReloadingCache[Int] = {
AsyncReloadingCache(0, reloadAfter) { _ =>
AsyncReloadingCache(0, cacheTxCountReloadPeriod) { _ =>
run(TransactionQueries.mainTransactions.length.result)(database.databaseConfig)
}
}

private val addressCount: AsyncReloadingCache[Int] = {
AsyncReloadingCache(0, cacheAddressCountReloadPeriod) { _ =>
run(TransactionQueries.numberOfActiveAddressesQuery())(database.databaseConfig)
}
}

private val holderEstimationCount: AsyncReloadingCache[Int] = {
AsyncReloadingCache(0, cacheAddressCountReloadPeriod) { _ =>
run(OutputQueries.estimateTotalHolders())(database.databaseConfig)
}
}

def getMainChainTxnCount(): Int =
mainChainTxnCount.get()

def getAddressCount(): Int =
addressCount.get()

def getHolderEstimationCount(): Int =
holderEstimationCount.get()

override def startSelfOnce(): Future[Unit] = {
mainChainTxnCount.expireAndReloadFuture().map(_ => ())
for {
_ <- mainChainTxnCount.expireAndReloadFuture()
_ <- addressCount.expireAndReloadFuture()
} yield ()
}

override def stopSelfOnce(): Future[Unit] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ object ExplorerConfig {
explorer.cacheRowCountReloadPeriod,
explorer.cacheBlockTimesReloadPeriod,
explorer.cacheLatestBlocksReloadPeriod,
explorer.cacheTxCountReloadPeriod,
explorer.cacheAddressCountReloadPeriod,
explorer.exportTxsNumberThreshold,
explorer.streamParallelism,
explorer.maxTimeIntervals,
Expand Down Expand Up @@ -209,6 +211,8 @@ object ExplorerConfig {
cacheRowCountReloadPeriod: FiniteDuration,
cacheBlockTimesReloadPeriod: FiniteDuration,
cacheLatestBlocksReloadPeriod: FiniteDuration,
cacheTxCountReloadPeriod: FiniteDuration,
cacheAddressCountReloadPeriod: FiniteDuration,
exportTxsNumberThreshold: Int,
streamParallelism: Int,
maxTimeIntervals: MaxTimeIntervals,
Expand Down Expand Up @@ -238,6 +242,8 @@ final case class ExplorerConfig private (
cacheRowCountReloadPeriod: FiniteDuration,
cacheBlockTimesReloadPeriod: FiniteDuration,
cacheLatestBlocksReloadPeriod: FiniteDuration,
cacheTxCountReloadPeriod: FiniteDuration,
cacheAddressCountReloadPeriod: FiniteDuration,
exportTxsNumberThreshold: Int,
streamParallelism: Int,
maxTimeInterval: ExplorerConfig.MaxTimeIntervals,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ trait Documentation
getReservedSupply,
getLockedSupply,
getTotalTransactions,
getTotalAddresses,
estimateTotalHolders,
getAverageBlockTime,
getHashrates,
getAllChainsTxCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,13 @@ object OutputQueries {
AND outputs.main_chain = true
AND inputs.block_hash IS NULL;
""".asAS[(Option[U256], Option[U256])].exactlyOne

def estimateTotalHolders(
)(implicit ec: ExecutionContext): DBActionR[Int] =
sql"""
SELECT COUNT(DISTINCT address)
FROM outputs
WHERE spent_finalized IS NULL
AND main_chain = true
""".asAS[Int].headOrNone.map(_.getOrElse(0))
}
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,14 @@ object TransactionQueries extends StrictLogging {
addresses map existing.contains
}

def numberOfActiveAddressesQuery(
)(implicit ec: ExecutionContext): DBActionR[Int] =
sql"""
SELECT COUNT(DISTINCT address)
FROM transaction_per_addresses
WHERE main_chain = true
""".asAS[Int].headOrNone.map(_.getOrElse(0))

/** Filters input addresses that exist in DB */
def filterExistingAddresses(addresses: Set[Address]): DBActionR[ArraySeq[Address]] =
if (addresses.isEmpty) {
Expand Down
Loading
0