You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tuweni.apache.org by to...@apache.org on 2021/08/03 06:51:01 UTC

[incubator-tuweni] branch main updated: Add client version stats

This is an automated email from the ASF dual-hosted git repository.

toulmean pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git


The following commit(s) were added to refs/heads/main by this push:
     new fb4ccf6  Add client version stats
     new eb826d0  Merge pull request #334 from atoulme/add_client_version_stats
fb4ccf6 is described below

commit fb4ccf64c8d824629f6fedc9c800127b7207662c
Author: Antoine Toulme <an...@lunar-ocean.com>
AuthorDate: Mon Aug 2 23:18:53 2021 -0700

    Add client version stats
---
 .../tuweni/eth/crawler/RelationalPeerRepository.kt | 13 +++++-
 .../rest/{ClientVersion.kt => ClientIdInfo.kt}     | 38 +++++++++++++---
 .../tuweni/eth/crawler/rest/ClientsService.kt      | 12 ++++-
 .../tuweni/eth/crawler/rest/ClientIdInfoTest.kt    | 53 ++++++++++++++++++++++
 4 files changed, 106 insertions(+), 10 deletions(-)

diff --git a/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/RelationalPeerRepository.kt b/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/RelationalPeerRepository.kt
index a1c166e..1c769df 100644
--- a/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/RelationalPeerRepository.kt
+++ b/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/RelationalPeerRepository.kt
@@ -32,7 +32,7 @@ import org.apache.tuweni.devp2p.Peer
 import org.apache.tuweni.devp2p.PeerRepository
 import org.apache.tuweni.devp2p.eth.Status
 import org.apache.tuweni.devp2p.parseEnodeUri
-import org.apache.tuweni.eth.crawler.rest.ClientVersion
+import org.apache.tuweni.eth.crawler.rest.ClientIdInfo
 import org.apache.tuweni.rlpx.wire.WireConnection
 import org.slf4j.LoggerFactory
 import java.net.URI
@@ -228,6 +228,7 @@ open class RelationalPeerRepository(
 
   private var clientIds: List<ClientInfo>? = null
   private var londonStats: ClientReadyStats? = null
+  private var clientsStats: Map<String, Map<String, Long>>? = null
   private val started = AtomicBoolean(false)
 
   fun start() {
@@ -241,18 +242,24 @@ open class RelationalPeerRepository(
         clientIds = newClientIds
         var londonReady = 0
         var total = 0
+        val newClientsStats = mutableMapOf<String, MutableMap<String, Long>>()
         newClientIds.forEach { newClientCount ->
           total += newClientCount.count
-          val clientVersion = ClientVersion(newClientCount.clientId)
+          val clientVersion = ClientIdInfo(newClientCount.clientId)
+          val versionStats = newClientsStats.computeIfAbsent(clientVersion.name) { mutableMapOf() }
+          val statsCount = versionStats[clientVersion.version] ?: 0
+          versionStats[clientVersion.version] = statsCount + newClientCount.count
           londonClientVersions[clientVersion.name().toLowerCase()]?.let { londonVersion ->
             if (clientVersion >= londonVersion) {
               londonReady += newClientCount.count
             }
           }
         }
+        clientsStats = newClientsStats
         londonStats = ClientReadyStats(total, londonReady)
         totalClientsGauge.record(total.toLong())
         clientCalculationsCounter.add(1)
+
         delay(clientsStatsDelay)
       }
     }
@@ -266,6 +273,8 @@ open class RelationalPeerRepository(
 
   internal fun getClientIds(): List<ClientInfo> = clientIds ?: listOf()
 
+  internal fun getClientStats(): Map<String, Map<String, Long>> = clientsStats ?: mapOf()
+
   internal fun getClientIdsInternal(): List<ClientInfo> {
     dataSource.connection.use { conn ->
       val sql = "select clients.clientId, count(clients.clientId) from (select nodeinfo.clientId, nodeInfo.createdAt from nodeinfo inner join (select identity, max(createdAt) as maxCreatedAt from nodeinfo group by identity) maxSeen on nodeinfo.identity = maxSeen.identity and nodeinfo.createdAt = maxSeen.maxCreatedAt) as clients where clients.createdAt > ? group by clients.clientId"
diff --git a/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientVersion.kt b/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientIdInfo.kt
similarity index 55%
rename from eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientVersion.kt
rename to eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientIdInfo.kt
index 2e73f3e..6246779 100644
--- a/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientVersion.kt
+++ b/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientIdInfo.kt
@@ -16,17 +16,41 @@
  */
 package org.apache.tuweni.eth.crawler.rest
 
-data class ClientVersion(val clientId: String) : Comparable<String> {
+data class ClientIdInfo(val clientId: String) : Comparable<String> {
 
-  val segments = clientId.split("/")
+  val name: String
+  val label: String
+  val version: String
+  val os: String
+  val compiler: String
+  init {
+    val segments = clientId.split("/")
+    if (segments.size == 4) {
+      name = segments[0]
+      label = ""
+      version = segments[1]
+      os = segments[2]
+      compiler = segments[3]
+    } else if (segments.size == 5) {
+      name = segments[0]
+      label = segments[1]
+      version = segments[2]
+      os = segments[3]
+      compiler = segments[4]
+    } else {
+      name = segments[0]
+      version = ""
+      label = ""
+      os = ""
+      compiler = ""
+    }
+  }
 
-  fun name() = segments[0]
+  fun name() = name
 
-  fun version() = segments.firstOrNull() {
-    it.startsWith("v")
-  }
+  fun version() = version
 
   override fun compareTo(other: String): Int {
-    return version()?.compareTo(other) ?: 1
+    return version().compareTo(other)
   }
 }
diff --git a/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientsService.kt b/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientsService.kt
index 7aea9c4..076d04a 100644
--- a/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientsService.kt
+++ b/eth-crawler/src/main/kotlin/org/apache/tuweni/eth/crawler/rest/ClientsService.kt
@@ -54,10 +54,20 @@ class ClientsService {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   @Path("london/stats")
-  fun getClientStats(): String {
+  fun getLondonStats(): String {
     val repo = context!!.getAttribute("repo") as RelationalPeerRepository
     val peers = repo.getLondonStats()
     val result = mapper.writeValueAsString(peers)
     return result
   }
+
+  @GET
+  @Produces(MediaType.APPLICATION_JSON)
+  @Path("stats")
+  fun getClientStats(): String {
+    val repo = context!!.getAttribute("repo") as RelationalPeerRepository
+    val stats = repo.getClientStats()
+    val result = mapper.writeValueAsString(stats)
+    return result
+  }
 }
diff --git a/eth-crawler/src/test/kotlin/org/apache/tuweni/eth/crawler/rest/ClientIdInfoTest.kt b/eth-crawler/src/test/kotlin/org/apache/tuweni/eth/crawler/rest/ClientIdInfoTest.kt
new file mode 100644
index 0000000..4cc3b94
--- /dev/null
+++ b/eth-crawler/src/test/kotlin/org/apache/tuweni/eth/crawler/rest/ClientIdInfoTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.eth.crawler.rest
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class ClientIdInfoTest {
+
+  @Test
+  fun testNoLabel() {
+    val clientInfo = ClientIdInfo("Parity-Ethereum/v2.7.2-stable-2662d19-20200206/x86_64-unknown-linux-gnu/rustc1.41.0")
+    assertEquals("Parity-Ethereum", clientInfo.name)
+    assertEquals("", clientInfo.label)
+    assertEquals("v2.7.2-stable-2662d19-20200206", clientInfo.version)
+    assertEquals("x86_64-unknown-linux-gnu", clientInfo.os)
+    assertEquals("rustc1.41.0", clientInfo.compiler)
+  }
+
+  @Test
+  fun testWithLabel() {
+    val clientInfo = ClientIdInfo("OpenEthereum/Bob Ross/v3.0.1-stable-8ca8089-20200601/x86_64-unknown-linux-gnu/rustc1.43.1")
+    assertEquals("OpenEthereum", clientInfo.name)
+    assertEquals("Bob Ross", clientInfo.label)
+    assertEquals("v3.0.1-stable-8ca8089-20200601", clientInfo.version)
+    assertEquals("x86_64-unknown-linux-gnu", clientInfo.os)
+    assertEquals("rustc1.43.1", clientInfo.compiler)
+  }
+
+  @Test
+  fun testMalformed() {
+    val clientInfo = ClientIdInfo("Foo Bar 1.23")
+    assertEquals("Foo Bar 1.23", clientInfo.name)
+    assertEquals("", clientInfo.label)
+    assertEquals("", clientInfo.version)
+    assertEquals("", clientInfo.os)
+    assertEquals("", clientInfo.compiler)
+  }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tuweni.apache.org
For additional commands, e-mail: commits-help@tuweni.apache.org