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
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)