This repository has been archived by the owner on Dec 13, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathbilling.go
90 lines (72 loc) · 2.1 KB
/
billing.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright 2022 Outcaste LLC. Licensed under the Sustainable License v1.0.
package billing
import (
"context"
"os"
"sync/atomic"
"time"
"github.com/golang/glog"
"github.com/outcaste-io/outserv/x"
"github.com/outcaste-io/ristretto/z"
"github.com/shirou/gopsutil/v3/process"
)
func Run(closer *z.Closer) {
initWallet()
go trackCPU(closer)
}
// One CPU is always considered to be used.
func minCores(a float64) float64 {
if a < 2.0 {
return 2.0
}
return a
}
var microCpuHour int64
const (
USDPerCpuHour = 0.07 // 7 cents per cpu-hour.
Minute = time.Minute // Using this indirection for debugging.
)
func CPUHours() float64 {
mch := atomic.LoadInt64(µCpuHour)
return float64(mch) / 1e6
}
func AccountedFor(cpuHours float64) {
mch := int64(cpuHours * 1e6)
atomic.AddInt64(µCpuHour, -mch)
}
func trackCPU(closer *z.Closer) {
defer closer.Done()
glog.Infof("Got PID for usage tracking: %d\n", os.Getpid())
proc, err := process.NewProcess(int32(os.Getpid()))
x.Checkf(err, "unable to track process")
// We need to call it upfront, so all the following calls would track CPU
// usage correctly. That's because every Percent call tracks the CPU usage
// since the last Percent call.
_, err = proc.Percent(0)
x.Checkf(err, "unable to track CPU usage")
tick := time.NewTicker(Minute)
defer tick.Stop()
for i := int64(1); ; i++ {
select {
case <-tick.C: // Track CPU usage every minute.
usage, err := proc.Percent(0)
x.Checkf(err, "unable to track CPU usage")
usage = usage / 100.0 // Convert percentage to the number of cpus.
usage = minCores(usage) // Minimum usage.
usage = usage / 60 // 60 mins in the hour.
atomic.AddInt64(µCpuHour, int64(usage*1e6))
case <-closer.HasBeenClosed():
glog.Infof("Billing exiting usage tracking.")
return
}
}
}
// Charge returns the amount charged for in dollars, and error if any.
func Charge(cpuHours float64) error {
usd := cpuHours * USDPerCpuHour
if err := wallet.Pay(context.Background(), usd); err != nil {
return err
}
glog.Infof("Charged $%.3f for %.3f CPU hours\n", usd, cpuHours)
return nil
}