Skip to content

Commit f5b725d

Browse files
committed
add a stats counter for average crawl staleness
1 parent 1eeac54 commit f5b725d

File tree

6 files changed

+105
-22
lines changed

6 files changed

+105
-22
lines changed

Sources/SwiftinitPages/Surfaces/Administration/Swiftinit.AdminPage.swift

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ extension Swiftinit
1717
public
1818
let requestsDropped:Int
1919
public
20+
let averagePackageStaleness:Duration
21+
public
2022
let errorsCrawling:Int
2123
public
2224
let reposCrawled:Int
@@ -35,6 +37,7 @@ extension Swiftinit
3537
@inlinable public
3638
init(configuration:Mongo.ReplicaSetConfiguration,
3739
requestsDropped:Int,
40+
averagePackageStaleness:Duration,
3841
errorsCrawling:Int,
3942
reposCrawled:Int,
4043
reposUpdated:Int,
@@ -46,6 +49,7 @@ extension Swiftinit
4649
self.configuration = configuration
4750

4851
self.requestsDropped = requestsDropped
52+
self.averagePackageStaleness = averagePackageStaleness
4953
self.errorsCrawling = errorsCrawling
5054
self.reposCrawled = reposCrawled
5155
self.reposUpdated = reposUpdated
@@ -250,19 +254,48 @@ extension Swiftinit.AdminPage:Swiftinit.AdministrativePage
250254

251255
main[.dl]
252256
{
257+
let uptime:Age = .init(self.tour.started.duration(to: .now))
258+
253259
$0[.dt] = "Uptime"
254-
$0[.dd] = "\(self.tour.started.duration(to: .now))"
260+
$0[.dd] = uptime.long
255261

256262
let requests:Int =
257263
self.tour.profile.requests.http1.total +
258264
self.tour.profile.requests.http2.total
265+
259266
$0[.dt] = "Requests"
260267
$0[.dd] = "\(requests)"
261268

262269
$0[.dt] = "Requests (Barbies)"
263270
$0[.dd] = "\(self.tour.profile.requests.http2.barbie)"
264271

272+
$0[.dt] = "requests dropped"
273+
$0[.dd] = "\(self.requestsDropped)"
274+
275+
let staleness:Duration = self.averagePackageStaleness
276+
$0[.dt] = "Average package staleness"
277+
$0[.dd] = "\(staleness)"
278+
279+
$0[.dt] = "GitHub crawling errors"
280+
$0[.dd] = "\(self.errorsCrawling)"
281+
282+
$0[.dt] = "GitHub repos crawled"
283+
$0[.dd] = "\(self.reposCrawled)"
284+
285+
$0[.dt] = "GitHub repos updated"
286+
$0[.dd] = "\(self.reposUpdated)"
287+
288+
$0[.dt] = "GitHub tags crawled"
289+
$0[.dd] = "\(self.tagsCrawled)"
290+
291+
$0[.dt] = "GitHub tags updated"
292+
$0[.dd] = "\(self.tagsUpdated)"
293+
}
265294

295+
main[.h2] = "Performance"
296+
297+
main[.dl]
298+
{
266299
if let last:ServerTour.Request = self.tour.lastImpression
267300
{
268301
$0[.h3] = "Last Impression"
@@ -321,24 +354,6 @@ extension Swiftinit.AdminPage:Swiftinit.AdministrativePage
321354

322355
$0[.dt] = "bytes transferred (content only)"
323356
$0[.dd] = "\(self.tour.profile.requests.bytes.total)"
324-
325-
$0[.dt] = "requests dropped"
326-
$0[.dd] = "\(self.requestsDropped)"
327-
328-
$0[.dt] = "GitHub crawling errors"
329-
$0[.dd] = "\(self.errorsCrawling)"
330-
331-
$0[.dt] = "GitHub repos crawled"
332-
$0[.dd] = "\(self.reposCrawled)"
333-
334-
$0[.dt] = "GitHub repos updated"
335-
$0[.dd] = "\(self.reposUpdated)"
336-
337-
$0[.dt] = "GitHub tags crawled"
338-
$0[.dd] = "\(self.tagsCrawled)"
339-
340-
$0[.dt] = "GitHub tags updated"
341-
$0[.dd] = "\(self.tagsUpdated)"
342357
}
343358

344359
main += self.tour.profile

Sources/SwiftinitServer/Endpoints/Interactive/Swiftinit.AdminDashboardEndpoint.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ extension Swiftinit.AdminDashboardEndpoint:RestrictedEndpoint
2323

2424
.init(configuration: configuration,
2525
requestsDropped: counters.requestsDropped.load(ordering: .relaxed),
26+
averagePackageStaleness: .milliseconds(counters.averagePackageStaleness.load(
27+
ordering: .relaxed)),
2628
errorsCrawling: counters.errorsCrawling.load(ordering: .relaxed),
2729
reposCrawled: counters.reposCrawled.load(ordering: .relaxed),
2830
reposUpdated: counters.reposUpdated.load(ordering: .relaxed),

Sources/SwiftinitServer/Plugins/GitHubCrawler.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ protocol GitHubCrawler
1212

1313
var api:GitHub.Client<GitHub.API> { get }
1414

15+
mutating
1516
func crawl(updating server:Swiftinit.ServerLoop,
1617
over connection:GitHub.Client<GitHub.API>.Connection,
1718
with session:Mongo.Session) async throws
1819
}
1920
extension GitHubCrawler
2021
{
22+
mutating
2123
func run(alongside server:Swiftinit.ServerLoop) async throws
2224
{
2325
while true

Sources/SwiftinitServer/Plugins/GitHubPlugin.RepoMonitor.swift

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,41 @@
1+
import BSON
12
import GitHubAPI
23
import GitHubClient
34
import MongoDB
45
import SemanticVersions
56
import UnidocDB
67
import UnidocRecords
78

9+
extension GitHubPlugin
10+
{
11+
struct Average
12+
{
13+
private(set)
14+
var total:Double
15+
private(set)
16+
var count:Int
17+
18+
init()
19+
{
20+
self.total = 0
21+
self.count = 0
22+
}
23+
}
24+
}
25+
extension GitHubPlugin.Average
26+
{
27+
mutating
28+
func insert(_ value:Double)
29+
{
30+
self.total += value
31+
self.count += 1
32+
}
33+
34+
var value:Double?
35+
{
36+
self.count > 0 ? self.total / Double.init(self.count) : nil
37+
}
38+
}
839
extension GitHubPlugin
940
{
1041
struct RepoMonitor
@@ -14,10 +45,15 @@ extension GitHubPlugin
1445
private
1546
let pat:String
1647

48+
private
49+
var staleness:Average
50+
1751
init(api:GitHub.Client<GitHub.API>, pat:String)
1852
{
1953
self.api = api
2054
self.pat = pat
55+
56+
self.staleness = .init()
2157
}
2258
}
2359
}
@@ -26,6 +62,7 @@ extension GitHubPlugin.RepoMonitor:GitHubCrawler
2662
static
2763
var interval:Duration { .seconds(30) }
2864

65+
mutating
2966
func crawl(updating server:Swiftinit.ServerLoop,
3067
over connection:GitHub.Client<GitHub.API>.Connection,
3168
with session:Mongo.Session) async throws
@@ -53,8 +90,27 @@ extension GitHubPlugin.RepoMonitor:GitHubCrawler
5390
repo: origin.name,
5491
pat: self.pat)
5592

93+
let now:BSON.Millisecond = .now()
94+
95+
staleness:
96+
if package.crawled != 0
97+
{
98+
// Not entirely accurate (leap seconds!!!), but good enough for stats.
99+
self.staleness.insert(Double.init(now.value - package.crawled.value))
100+
101+
guard
102+
let average:Double = self.staleness.value
103+
else
104+
{
105+
break staleness
106+
}
107+
108+
server.atomics.averagePackageStaleness.store(Int.init(average),
109+
ordering: .relaxed)
110+
}
111+
56112
package.repo = try .github(response.repo)
57-
package.crawled = .now()
113+
package.crawled = now
58114

59115
switch try await server.db.packages.update(metadata: package, with: session)
60116
{

Sources/SwiftinitServer/Server/Swiftinit.Counters.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ extension Swiftinit
66
{
77
let requestsDropped:UnsafeAtomic<Int>
88

9+
/// Has units of milliseconds, but we don’t have a way to type that with `UnsafeAtomic`.
10+
let averagePackageStaleness:UnsafeAtomic<Int>
911
let errorsCrawling:UnsafeAtomic<Int>
1012
let reposCrawled:UnsafeAtomic<Int>
1113
let reposUpdated:UnsafeAtomic<Int>
@@ -15,6 +17,8 @@ extension Swiftinit
1517
init()
1618
{
1719
self.requestsDropped = .create(0)
20+
21+
self.averagePackageStaleness = .create(0)
1822
self.errorsCrawling = .create(0)
1923
self.reposCrawled = .create(0)
2024
self.reposUpdated = .create(0)
@@ -25,6 +29,8 @@ extension Swiftinit
2529
deinit
2630
{
2731
self.requestsDropped.destroy()
32+
33+
self.averagePackageStaleness.destroy()
2834
self.errorsCrawling.destroy()
2935
self.reposCrawled.destroy()
3036
self.reposUpdated.destroy()

Sources/SwiftinitServer/Swiftinit.ServerLoop.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,13 @@ extension Swiftinit.ServerLoop
148148
{
149149
tasks.addTask
150150
{
151-
try await plugin.telescope.run(alongside: self)
151+
var telescope:GitHubPlugin.RepoTelescope = plugin.telescope
152+
try await telescope.run(alongside: self)
152153
}
153154
tasks.addTask
154155
{
155-
try await plugin.monitor.run(alongside: self)
156+
var monitor:GitHubPlugin.RepoMonitor = plugin.monitor
157+
try await monitor.run(alongside: self)
156158
}
157159
}
158160
if let plugin:Swiftinit.PluginIntegration<PolicyPlugin> = self.plugins.policy

0 commit comments

Comments
 (0)