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)