Hi folks,
I thought I’d see how far I could get at creating a Prometheus client for Julia, to cut my teeth and learn about package development. This involves juggling stateful objects (metrics) which are mutated in place. I’ve got the very beginnings of a repo here but the stripped-down core idea is below:
mutable struct MetricValue
value::Float64
lock::ReentrantLock
end
abstract type Metric end
mutable struct Gauge <: Metric
name::String
value::MetricValue
end
function inc(metric::Metric, amount::Real = 1.0)
@async begin
lock(metric.value.lock)
metric.value.value = metric.value.value + amount
unlock(metric.value.lock)
end
return nothing
end
function get(metric::Metric)
lock(metric.value.lock)
current_value = metric.value.value
unlock(metric.value.lock)
current_value
end
function Gauge(name::String; value::Real = 0)
Gauge(name, MetricValue(value))
end
Now, the following code snippet works just fine in a repl:
using Test
g = Gauge("test") # initialised with a value of 0
inc(g)
@test get(g) == 1.0
But when I try to place this snippet in a test (as commented out in my repository) I get entirely different behaviour: the inc(g)
has no effect at all, and the value of g
stays at its initial value of 0.0.
Normally I’d attribute this to my lack of understanding of how scoping works in Julia, but the fact that everything is okay in a REPL really throws me off here. Do you know what I might be doing wrong?
Additionally, have I handled the @async
and lock
stuff as I should have? The idea I was going for is to do all metric updating asynchronously, but if two attempts to manipulate a single metric clash then the second one has to wait its turn.
Julia Version 1.5.0
Commit 96786e22cc (2020-08-01 23:44 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-9.0.1 (ORCJIT, skylake)