为什么我的两个相同的 systemctl 定时器会产生不同数量的输出?

Why procduces my two identical systemctl timers different amount of outputs?

我有两个 raspberry pi 几乎相同的设置。 在两者上,我都有一个 systemctl 服务和一个每 15 秒 运行 的计时器。 该作业调用一个 go 脚本,该脚本读取系统温度并在我的 mariadb 数据库中创建一个日志条目。数据库 运行ning 在其中一个覆盆子上。因此,两个 go 脚本的唯一区别是数据库的主机名和标识符变量,因此我可以在数据库中看到数据库条目来自哪个数据库。其余相同。

问题是 raspberry1 生成的数据库条目比 raspberry2 多。 当我从一个零条目的新数据库开始时。然后 1 有来自 raspberry1 的大约 120 个条目和来自 raspberrie 2 的大约 70 个条目。错误继续。更多时间后,我得到例如 1000 个条目和 700 个条目。

有谁知道差异可能来自哪里? 也许当两个作业都想同时在 mariadb 中创建一个新条目时? 但我几乎可以肯定这是由 mariadb 以某种方式处理的。

详情如下:

FanLog.service

[Unit]
Description=Lüfter Temperatur Logger Service
After=network.target

[Service]
ExecStart=/root/go/temperatur_logger
WorkingDirectory=/root/go
StandardOutput=append:/var/log/TempLogger.log
StandardError=append:/var/log/TempLogger_error.log
User=root

FanLog.timer

[Unit]
Description=Fanlogger Timer

[Timer]
# OnBootSec=15min
# Wochentage-dd-mm hh:mm:ss
# OnCalendar=*-*-* *:05:00
# OnUnitActiveSec=1min
# OnCalendar=minutely
OnCalendar=*-*-* *:*:0/15
# OnCalendar=*-*-* *:0/0:20

[Install]
WantedBy=timers.target

“systemctl status FanLog.timer”的输出似乎没问题。 我最多可以看到 15 秒缩短到几毫秒,如果我重复,我又可以看到 15 秒。

 ⚡ root@DeskPi  /etc/systemd/system  systemctl status FanLog.timer
● FanLog.timer - Fanlogger Timer
   Loaded: loaded (/etc/systemd/system/FanLog.timer; enabled; vendor preset: enabled)
   Active: active (waiting) since Mon 2021-09-20 06:07:22 CEST; 39min ago
  Trigger: Mon 2021-09-20 06:46:45 CEST; 13s left

Sep 20 06:07:22 DeskPi systemd[1]: Stopping Fanlogger Timer.
Sep 20 06:07:22 DeskPi systemd[1]: Started Fanlogger Timer.

temperatur_logger.go

package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
    "os"
    "os/exec"
    "time"

    "github.com/stianeikeland/go-rpio"

    _ "github.com/go-sql-driver/mysql"
)

const (
    username   = "TempLogger"
    password   = "XXXXXXXXXXXXX"
    hostname   = "192.168.1.20:3306" // Different on other device
    dbname     = "TempLog"
    table      = "TempLog"
    DeviceName = "OctoPi" // Different on other device
)

var (
    // Use mcu pin 24, corresponds to GPIO3 on the pi
    pin = rpio.Pin(24)
)

func add(x int, y int) int {
    return x + y
}

func get_temp(temperatur string) string {
    var outputlaenge int
    var aktuelle_temperatur string
    // Befehl
    cmd := exec.Command("sudo", "/usr/bin/vcgencmd", "measure_temp")
    // run command
    if output, err := cmd.Output(); err != nil {
        fmt.Println("Error", err)
    } else {

        outputlaenge = len(output)
        aktuelle_temperatur = string(output[outputlaenge-7 : outputlaenge-3])
        // fmt.Println(string(output[outputlaenge-7 : outputlaenge-3]))
    }
    return aktuelle_temperatur
}

func dsn(dbName string) string {
    return fmt.Sprintf("%s:%s@tcp(%s)/%s", username, password, hostname, dbName)
}

func dbConnection() (*sql.DB, error) {
    db, err := sql.Open("mysql", dsn(""))
    if err != nil {
        log.Printf("Error %s when opening DB\n", err)
        return nil, err
    }
    defer db.Close()

    ctx, cancelfunc := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancelfunc()
    if err != nil {
        log.Printf("Error %s when creating DB\n", err)
        return nil, err
    }

    db.Close()
    db, err = sql.Open("mysql", dsn(dbname))
    if err != nil {
        log.Printf("Error %s when opening DB", err)
        return nil, err
    }

    db.SetMaxOpenConns(20)
    db.SetMaxIdleConns(20)
    db.SetConnMaxLifetime(time.Minute * 5)

    ctx, cancelfunc = context.WithTimeout(context.Background(), 5*time.Second)
    defer cancelfunc()
    err = db.PingContext(ctx)
    if err != nil {
        log.Printf("Errors %s pinging DB", err)
        return nil, err
    }
    log.Printf("Connected to DB %s successfully\n", dbname)
    return db, nil
}

type temperatur_eintrag struct {
    DeviceName  string
    record_date string
    temperature string
    fan         bool
}

func insert(db *sql.DB, p temperatur_eintrag) error {
    query := "INSERT INTO " + table + "(device_name, record_date, temperature, fan) VALUES (?, ?, ?, ?)"
    ctx, cancelfunc := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancelfunc()
    stmt, err := db.PrepareContext(ctx, query)
    if err != nil {
        log.Printf("Error %s when preparing SQL statement", err)
        return err
    }
    defer stmt.Close()
    res, err := stmt.ExecContext(ctx, p.DeviceName, p.record_date, p.temperature, p.fan)
    if err != nil {
        log.Printf("Error %s when inserting row into products table", err)
        return err
    }
    rows, err := res.RowsAffected()
    if err != nil {
        log.Printf("Error %s when finding rows affected", err)
        return err
    }
    log.Printf("%d log entry created ", rows)
    return nil
}

func main() {
    currentTime := time.Now()
    var tempi string
    var myFan bool

    db, err := dbConnection()
    if err != nil {
        log.Printf("Error %s when getting db connection", err)
        return
    }
    defer db.Close()
    log.Printf("Successfully connected to database")

    // Open and map memory to access gpio, check for errors
    if err := rpio.Open(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    // Unmap gpio memory when done
    defer rpio.Close()
    // pin.PullUp()

    if pin.Read() == 1 {
        fmt.Println("Pin High")
        myFan = true
    } else {
        fmt.Println("Pin Low")
        myFan = false
    }

    p := temperatur_eintrag{
        DeviceName:  DeviceName,
        record_date: currentTime.Format("2006-01-02 15:04:05"),
        temperature: get_temp(tempi),
        fan:         myFan,
    }
    err = insert(db, p)
    if err != nil {
        log.Printf("Insert failed with error %s", err)
        return
    }
}

我找到了解决方案:

这两个选项之一使它起作用:

Persistent=true
AccuracySec=2sec

不确定是哪一个,因为我没有测试更多。

FanLog.timer 现在看起来是这样:

[Unit]
Description=Fanlogger Timer

[Timer]
OnCalendar=*:*:0/10
Persistent=true
AccuracySec=2sec

[Install]
WantedBy=timers.target