Skip to content
This repository was archived by the owner on Oct 14, 2021. It is now read-only.
Merged
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package net.adoptopenjdk.api.v3;
package net.adoptopenjdk.api.v3

import net.adoptopenjdk.api.v3.dataSources.APIDataStore
import net.adoptopenjdk.api.v3.dataSources.ApiPersistenceFactory
import net.adoptopenjdk.api.v3.dataSources.persitence.ApiPersistence
import net.adoptopenjdk.api.v3.models.DbStatsEntry
import net.adoptopenjdk.api.v3.models.DownloadDiff
import net.adoptopenjdk.api.v3.models.DownloadStats
Expand All @@ -18,10 +19,17 @@ class StatEntry(
val count: Long
)

class DownloadStatsInterface {
private val dataStore = ApiPersistenceFactory.get()
class DownloadStatsInterface(
private val dataStore: ApiPersistence = ApiPersistenceFactory.get()
) {

suspend fun getTrackingStats(days: Int?, from: ZonedDateTime?, to: ZonedDateTime?, source: StatsSource?, featureVersion: Int?, dockerRepo: String?): List<DownloadDiff> {
suspend fun getTrackingStats(
days: Int? = null,
from: ZonedDateTime? = null,
to: ZonedDateTime? = null,
source: StatsSource? = null,
featureVersion: Int? = null,
dockerRepo: String? = null): List<DownloadDiff> {

//need +1 as for a diff you need num days +1 from db
val daysSince = (days ?: 30) + 1
Expand Down Expand Up @@ -84,15 +92,33 @@ class DownloadStatsInterface {
return sumDailyStats(
dataStore
.getGithubStats(start, end)
.groupBy { it.date.toLocalDate() }
.flatMap { grouped ->
grouped.value
.groupBy { it.feature_version }
.map { featureVersionsForDay ->
featureVersionsForDay.value.maxBy { it.date }!!
}
}
.filter { featureVersion == null || it.feature_version == featureVersion }
.sortedBy { it.date }
)
}

private suspend fun getDockerDownloadStatsByDate(start: ZonedDateTime, end: ZonedDateTime, dockerRepo: String?): List<StatEntry> {
return sumDailyStats(
dataStore
.getDockerStats(start, end)
.groupBy { it.date.toLocalDate() }
.flatMap { grouped ->
grouped.value
.groupBy { it.repo }
.map { repoForDay ->
repoForDay.value.maxBy { it.date }!!
}
}
.filter { dockerRepo == null || it.repo == dockerRepo }
.sortedBy { it.date }
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package net.adoptopenjdk.api.v3
import org.apache.http.HttpRequest
import org.apache.http.HttpResponse
import org.apache.http.client.RedirectStrategy
import org.apache.http.client.config.RequestConfig
import org.apache.http.client.methods.HttpUriRequest
import org.apache.http.impl.NoConnectionReuseStrategy
import org.apache.http.impl.nio.client.HttpAsyncClients
Expand All @@ -13,10 +14,19 @@ object HttpClientFactory {
private val client: HttpAsyncClient
private val noRedirect: HttpAsyncClient

val REQUEST_CONFIG = RequestConfig
.copy(RequestConfig.DEFAULT)
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.setConnectionRequestTimeout(5000)
.build()!!

init {

val client = HttpAsyncClients.custom()
.setConnectionReuseStrategy(NoConnectionReuseStrategy())
.disableCookieManagement()
.setDefaultRequestConfig(REQUEST_CONFIG)
.build()
client.start()
this.client = client
Expand All @@ -33,6 +43,7 @@ object HttpClientFactory {

})
.setConnectionReuseStrategy(NoConnectionReuseStrategy())
.setDefaultRequestConfig(REQUEST_CONFIG)
.build()
noRedirect.start()
this.noRedirect = noRedirect
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package net.adoptopenjdk.api.v3.dataSources

import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeout
import net.adoptopenjdk.api.v3.HttpClientFactory
import net.adoptopenjdk.api.v3.dataSources.github.GithubAuth
import org.apache.commons.io.IOUtils
import org.apache.http.HttpResponse
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.RequestBuilder
import org.apache.http.concurrent.FutureCallback
import org.slf4j.LoggerFactory
import java.io.StringWriter
Expand Down Expand Up @@ -37,7 +38,7 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient {
@JvmStatic
private val LOGGER = LoggerFactory.getLogger(this::class.java)
private val TOKEN: String? = GithubAuth.readToken()

private const val REQUEST_TIMEOUT = 12000L

fun extractBody(response: HttpResponse?): String? {
if (response == null) {
Expand Down Expand Up @@ -98,7 +99,10 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient {
private fun getData(urlRequest: UrlRequest, continuation: Continuation<HttpResponse>) {
try {
val url = URL(urlRequest.url)
val request = HttpGet(url.toURI())
val request = RequestBuilder
.get(url.toURI())
.setConfig(HttpClientFactory.REQUEST_CONFIG)
.build()

if (urlRequest.lastModified != null) {
request.addHeader("If-Modified-Since", urlRequest.lastModified)
Expand All @@ -116,6 +120,7 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient {
}

client.execute(request, ResponseHandler(this, continuation, urlRequest))

} catch (e: Exception) {
continuation.resumeWith(Result.failure(e))
}
Expand All @@ -127,8 +132,10 @@ class DefaultUpdaterHtmlClient : UpdaterHtmlClient {
for (retryCount in 1..10) {
try {
LOGGER.info("Getting ${request.url} ${request.lastModified}")
val response: HttpResponse = suspendCoroutine { continuation ->
getData(request, continuation)
val response: HttpResponse = withTimeout(REQUEST_TIMEOUT) {
suspendCoroutine<HttpResponse> { continuation ->
getData(request, continuation)
}
}
LOGGER.info("Got ${request.url}")
return response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class DockerStatsInterface {

private fun getStatsForUrl(url: String): JsonObject {
return runBlocking {
val stats = UpdaterHtmlClientFactory.client.get(url);
val stats = UpdaterHtmlClientFactory.client.get(url)
if (stats == null) {
throw Exception("Stats not returned")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package net.adoptopenjdk.api.v3.stats

import kotlinx.coroutines.runBlocking
import net.adoptopenjdk.api.v3.DownloadStatsInterface
import net.adoptopenjdk.api.v3.dataSources.ApiPersistenceFactory
import net.adoptopenjdk.api.v3.dataSources.models.AdoptRepos
import net.adoptopenjdk.api.v3.dataSources.persitence.ApiPersistence
import net.adoptopenjdk.api.v3.models.StatsSource
import org.slf4j.LoggerFactory
import java.time.ZoneOffset
import java.time.ZonedDateTime

class StatsInterface {

Expand All @@ -30,17 +30,36 @@ class StatsInterface {
}

private suspend fun removeBadDownloadStats() {
val tracking = downloadStatsInterface.getTrackingStats(10, null, null, StatsSource.all, null, null)
val tracking = downloadStatsInterface.getTrackingStats(days = 10, source = StatsSource.all)
tracking
.filter { it.daily <= 0 }
.forEach { entry ->
val start = entry.date.toLocalDate().atStartOfDay()
val end = entry.date.toLocalDate().plusDays(1).atStartOfDay()
LOGGER.info("Removing bad stats between $start $end")
runBlocking {
database.removeStatsBetween(start.atZone(ZoneOffset.UTC), end.atZone(ZoneOffset.UTC))
}
val start = entry.date.toLocalDate().atStartOfDay().atZone(ZoneOffset.UTC)
val end = entry.date.toLocalDate().plusDays(1).atStartOfDay().atZone(ZoneOffset.UTC)

printStatDebugInfo(start, end)
database.removeStatsBetween(start, end)
}
}

private suspend fun printStatDebugInfo(start: ZonedDateTime, end: ZonedDateTime) {
LOGGER.info("Removing bad stats between $start $end")
printStats(start, end)
LOGGER.info("Day before: $start $end")
printStats(start.minusDays(1), end.minusDays(1))
}

private suspend fun printStats(start: ZonedDateTime, end: ZonedDateTime) {
database
.getGithubStats(start, end)
.forEach { stat ->
LOGGER.info("github stat: ${stat.feature_version} ${stat.downloads}")
}

database
.getDockerStats(start, end)
.forEach { stat ->
LOGGER.info("docker stat: ${stat.repo} ${stat.pulls}")
}
}
}
4 changes: 1 addition & 3 deletions adoptopenjdk-api-v3-updater/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<configuration>
<logger name="org.apache.http.client.protocol.ResponseProcessCookies" level="ERROR"/>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line]%n%msg%n</pattern>
</encoder>
</appender>


<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/tmp/updater.log</file>

<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line]%n%msg%n</pattern>
</encoder>
</appender>


<root level="info">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
package net.adoptopenjdk.api

import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import net.adoptopenjdk.api.v3.DownloadStatsInterface
import net.adoptopenjdk.api.v3.dataSources.ApiPersistenceFactory
import net.adoptopenjdk.api.v3.dataSources.DefaultUpdaterHtmlClient
import net.adoptopenjdk.api.v3.dataSources.UpdaterHtmlClientFactory
import net.adoptopenjdk.api.v3.dataSources.persitence.ApiPersistence
import net.adoptopenjdk.api.v3.models.DockerDownloadStatsDbEntry
import net.adoptopenjdk.api.v3.models.GithubDownloadStatsDbEntry
import net.adoptopenjdk.api.v3.models.StatsSource
import net.adoptopenjdk.api.v3.stats.DockerStatsInterface
import org.junit.Assert
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import java.time.ZonedDateTime
import kotlin.test.assertEquals


class DockerStatsInterfaceTest {
Expand All @@ -30,5 +39,42 @@ class DockerStatsInterfaceTest {
Assert.assertTrue(stats.size > 0)
}
}


@Test
fun onlyLastStatEntryPerDayIsRead() {
runBlocking {
val apiPersistanceMock = mockk<ApiPersistence>()
coEvery { apiPersistanceMock.getGithubStats(any<ZonedDateTime>(), any<ZonedDateTime>()) } returns listOf(
GithubDownloadStatsDbEntry(ZonedDateTime.now().minusMinutes(10), 100, 8),
GithubDownloadStatsDbEntry(ZonedDateTime.now().minusMinutes(20), 90, 8),
GithubDownloadStatsDbEntry(ZonedDateTime.now().minusDays(1), 80, 8),
GithubDownloadStatsDbEntry(ZonedDateTime.now().minusMinutes(15), 20, 9)
)

coEvery { apiPersistanceMock.getDockerStats(any<ZonedDateTime>(), any<ZonedDateTime>()) } returns listOf(
DockerDownloadStatsDbEntry(ZonedDateTime.now().minusMinutes(10), 100, "a-stats-repo"),
DockerDownloadStatsDbEntry(ZonedDateTime.now().minusMinutes(20), 90, "a-stats-repo"),
DockerDownloadStatsDbEntry(ZonedDateTime.now().minusDays(1), 80, "a-stats-repo"),
DockerDownloadStatsDbEntry(ZonedDateTime.now().minusMinutes(15), 20, "a-different-stats-repo")
)

val downloadStatsInterface = DownloadStatsInterface(apiPersistanceMock)

var stats = downloadStatsInterface.getTrackingStats(10, source = StatsSource.github, featureVersion = 8)
assertEquals(100, stats[0].total)
stats = downloadStatsInterface.getTrackingStats(10, source = StatsSource.github)
assertEquals(120, stats[0].total)

stats = downloadStatsInterface.getTrackingStats(10, source = StatsSource.dockerhub, dockerRepo = "a-stats-repo")
assertEquals(100, stats[0].total)
stats = downloadStatsInterface.getTrackingStats(10, source = StatsSource.dockerhub)
assertEquals(120, stats[0].total)

stats = downloadStatsInterface.getTrackingStats(10)
assertEquals(240, stats[0].total)

}
}
}