mirror of
https://github.com/criyle/go-judge.git
synced 2025-09-26 22:39:12 +08:00

Some checks failed
Build / Goreleaser (push) Has been cancelled
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (amd64_v3, darwin) (push) Has been cancelled
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (amd64_v3, linux) (push) Has been cancelled
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (amd64_v3, windows) (push) Has been cancelled
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (arm64_v8.0, darwin) (push) Has been cancelled
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (arm64_v8.0, linux) (push) Has been cancelled
Build / Upload artifacts-${{ matrix.os }}-${{ matrix.arch }} (arm64_v8.0, windows) (push) Has been cancelled
267 lines
6.2 KiB
Go
267 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/criyle/go-judge/env/pool"
|
|
"github.com/criyle/go-judge/envexec"
|
|
"github.com/criyle/go-judge/filestore"
|
|
"github.com/criyle/go-judge/worker"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
const (
|
|
metricsNamespace = "go_judge"
|
|
execSubsystem = "exec"
|
|
filestoreSubsystem = "file"
|
|
environmentSubsystem = "environment"
|
|
workerSubsystem = "worker"
|
|
)
|
|
|
|
var (
|
|
// 1ms -> 100s
|
|
timeBuckets = []float64{
|
|
0.001, 0.002, 0.005, 0.010, 0.025, 0.050, 0.1, 0.2,
|
|
0.4, 0.8, 1.0, 2, 5, 10, 20, 50, 100,
|
|
}
|
|
|
|
// 4k (1<<12) -> 4g (1<<32)
|
|
memoryBucket = prometheus.ExponentialBuckets(1<<12, 2, 21)
|
|
// 256 byte (1<<8) -> 256m (1<<28)
|
|
fileSizeBucket = prometheus.ExponentialBuckets(1<<8, 2, 20)
|
|
|
|
execErrorCount = prometheus.NewCounter(prometheus.CounterOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: execSubsystem,
|
|
Name: "error_count",
|
|
Help: "Number of exec query returns error",
|
|
})
|
|
|
|
execTimeHist = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: execSubsystem,
|
|
Name: "time_seconds",
|
|
Help: "Histogram for the command execution time",
|
|
Buckets: timeBuckets,
|
|
}, []string{"status"})
|
|
|
|
execMemHist = prometheus.NewHistogramVec(prometheus.HistogramOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: execSubsystem,
|
|
Name: "memory_bytes",
|
|
Help: "Histogram for the command execution max memory",
|
|
Buckets: memoryBucket,
|
|
}, []string{"status"})
|
|
|
|
fsSizeHist = prometheus.NewHistogram(prometheus.HistogramOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: filestoreSubsystem,
|
|
Name: "size_bytes",
|
|
Help: "Histogram for the file size created in the file store",
|
|
Buckets: fileSizeBucket,
|
|
})
|
|
|
|
fsCurrentTotalCount = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: filestoreSubsystem,
|
|
Name: "current_bytes_count",
|
|
Help: "Total number of current files in the file store",
|
|
})
|
|
|
|
fsCurrentTotalSize = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: filestoreSubsystem,
|
|
Name: "current_bytes_sum",
|
|
Help: "Total size of current files in the file store",
|
|
})
|
|
|
|
envCreated = prometheus.NewCounter(prometheus.CounterOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: environmentSubsystem,
|
|
Name: "count",
|
|
Help: "Total number of environment build by environment builder",
|
|
})
|
|
|
|
envInUse = prometheus.NewGauge(prometheus.GaugeOpts{
|
|
Namespace: metricsNamespace,
|
|
Subsystem: environmentSubsystem,
|
|
Name: "current_count",
|
|
Help: "Total number of environment currently in use",
|
|
})
|
|
|
|
workerQueue = prometheus.NewDesc(
|
|
prometheus.BuildFQName(metricsNamespace, workerSubsystem, "queue_count"),
|
|
"Number of requests waiting in worker queue", nil, nil,
|
|
)
|
|
|
|
workerRunning = prometheus.NewDesc(
|
|
prometheus.BuildFQName(metricsNamespace, workerSubsystem, "running_count"),
|
|
"Number of request running by workers", nil, nil,
|
|
)
|
|
)
|
|
|
|
func init() {
|
|
prometheus.MustRegister(execErrorCount)
|
|
prometheus.MustRegister(execTimeHist)
|
|
prometheus.MustRegister(execMemHist)
|
|
prometheus.MustRegister(fsSizeHist, fsCurrentTotalCount, fsCurrentTotalSize)
|
|
prometheus.MustRegister(envCreated, envInUse)
|
|
}
|
|
|
|
func execObserve(res worker.Response) {
|
|
if res.Error != nil {
|
|
execErrorCount.Inc()
|
|
}
|
|
for _, r := range res.Results {
|
|
status := r.Status.String()
|
|
time := r.Time.Seconds()
|
|
memory := float64(r.Memory)
|
|
|
|
execTimeHist.WithLabelValues(status).Observe(time)
|
|
execMemHist.WithLabelValues(status).Observe(memory)
|
|
}
|
|
}
|
|
|
|
var _ filestore.FileStore = &metricsFileStore{}
|
|
|
|
type metricsFileStore struct {
|
|
mu sync.Mutex
|
|
filestore.FileStore
|
|
fileSize map[string]int64
|
|
}
|
|
|
|
func newMetricsFileStore(fs filestore.FileStore) filestore.FileStore {
|
|
store := &metricsFileStore{
|
|
FileStore: fs,
|
|
fileSize: make(map[string]int64),
|
|
}
|
|
fi := store.List()
|
|
for id := range fi {
|
|
_, file := store.Get(id)
|
|
if file == nil {
|
|
continue
|
|
}
|
|
if f, ok := file.(*envexec.FileInput); ok {
|
|
info, err := os.Stat(f.Path)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
store.fileSize[id] = info.Size()
|
|
sf := float64(info.Size())
|
|
fsSizeHist.Observe(sf)
|
|
fsCurrentTotalSize.Add(sf)
|
|
fsCurrentTotalCount.Inc()
|
|
}
|
|
}
|
|
return store
|
|
}
|
|
|
|
func (m *metricsFileStore) Add(name, path string) (string, error) {
|
|
id, err := m.FileStore.Add(name, path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
return id, nil
|
|
}
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
s := fi.Size()
|
|
m.fileSize[id] = s
|
|
|
|
sf := float64(s)
|
|
fsSizeHist.Observe(sf)
|
|
fsCurrentTotalSize.Add(sf)
|
|
fsCurrentTotalCount.Inc()
|
|
|
|
return id, nil
|
|
}
|
|
|
|
func (m *metricsFileStore) Remove(id string) bool {
|
|
success := m.FileStore.Remove(id)
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
s, ok := m.fileSize[id]
|
|
if !ok {
|
|
return success
|
|
}
|
|
delete(m.fileSize, id)
|
|
|
|
sf := float64(s)
|
|
fsCurrentTotalSize.Sub(sf)
|
|
fsCurrentTotalCount.Dec()
|
|
|
|
return success
|
|
}
|
|
|
|
var _ pool.EnvBuilder = &metricsEnvBuilder{}
|
|
|
|
type metricsEnvBuilder struct {
|
|
pool.EnvBuilder
|
|
}
|
|
|
|
func (b *metricsEnvBuilder) Build() (pool.Environment, error) {
|
|
e, err := b.EnvBuilder.Build()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
envCreated.Inc()
|
|
return e, nil
|
|
}
|
|
|
|
var _ worker.EnvironmentPool = &metricsEnvPool{}
|
|
|
|
type metricsEnvPool struct {
|
|
worker.EnvironmentPool
|
|
}
|
|
|
|
func (p *metricsEnvPool) Get() (envexec.Environment, error) {
|
|
e, err := p.EnvironmentPool.Get()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
envInUse.Inc()
|
|
return e, nil
|
|
}
|
|
|
|
func (p *metricsEnvPool) Put(env envexec.Environment) {
|
|
p.EnvironmentPool.Put(env)
|
|
envInUse.Dec()
|
|
}
|
|
|
|
var _ worker.Worker = &metricsWorker{}
|
|
var _ prometheus.Collector = &metricsWorker{}
|
|
|
|
type metricsWorker struct {
|
|
worker.Worker
|
|
}
|
|
|
|
// Collect implements prometheus.Collector.
|
|
func (m *metricsWorker) Collect(ch chan<- prometheus.Metric) {
|
|
s := m.Stat()
|
|
ch <- prometheus.MustNewConstMetric(
|
|
workerQueue, prometheus.GaugeValue, float64(s.Queue),
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
workerRunning, prometheus.GaugeValue, float64(s.Running),
|
|
)
|
|
}
|
|
|
|
// Describe implements prometheus.Collector.
|
|
func (m *metricsWorker) Describe(ch chan<- *prometheus.Desc) {
|
|
prometheus.DescribeByCollect(m, ch)
|
|
}
|
|
|
|
func newMetricsWorker(w worker.Worker) worker.Worker {
|
|
rt := &metricsWorker{w}
|
|
prometheus.MustRegister(rt)
|
|
return rt
|
|
}
|