ignore duplicate data points

This commit is contained in:
marco 2024-03-08 13:35:10 +01:00
parent c325c2765d
commit d9a3819ef5
4 changed files with 57 additions and 15 deletions

View file

@ -131,11 +131,13 @@ func (c *Controller) UsageMetrics(gctx *gin.Context) {
}
if _, err := c.DBClient.CreateMetric(generatedType, generatedBy, collectedAt, string(jsonPayload)); err != nil {
log.Errorf("Failed to store usage metrics: %s", err)
log.Error(err)
c.HandleDBErrors(gctx, err)
return
}
// empty body
// if CreateMetrics() returned nil, the metric was already there, we're good
// and don't split hair about 201 vs 200/204
gctx.Status(http.StatusCreated)
}

View file

@ -12,6 +12,11 @@ import (
"github.com/crowdsecurity/go-cs-lib/ptr"
)
const (
defaultMetricsInterval = 30 * time.Minute
minimumMetricsInterval = 15 * time.Minute
)
// CrowdsecServiceCfg contains the location of parsers/scenarios/... and acquisition files
type CrowdsecServiceCfg struct {
Enable *bool `yaml:"enable"`
@ -143,11 +148,6 @@ func (c *Config) LoadCrowdsec() error {
return nil
}
const (
defaultMetricsInterval = 30 * time.Second
minimumMetricsInterval = 15 * time.Second
)
func (c *CrowdsecServiceCfg) setMetricsInterval() {
switch {
case c.MetricsInterval == nil:

View file

@ -7,15 +7,24 @@ import (
"github.com/go-co-op/gocron"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/go-cs-lib/ptr"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/alert"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/bouncer"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/decision"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/event"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/machine"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/metric"
"github.com/crowdsecurity/crowdsec/pkg/types"
)
const (
// how long to keep metrics in the local database
defaultMetricsMaxAge = 7 * 24 * time.Hour
flushInterval = 1 * time.Minute
)
func (c *Client) StartFlushScheduler(config *csconfig.FlushDBCfg) (*gocron.Scheduler, error) {
maxItems := 0
@ -32,7 +41,7 @@ func (c *Client) StartFlushScheduler(config *csconfig.FlushDBCfg) (*gocron.Sched
// Init & Start cronjob every minute for alerts
scheduler := gocron.NewScheduler(time.UTC)
job, err := scheduler.Every(1).Minute().Do(c.FlushAlerts, maxAge, maxItems)
job, err := scheduler.Every(flushInterval).Do(c.FlushAlerts, maxAge, maxItems)
if err != nil {
return nil, fmt.Errorf("while starting FlushAlerts scheduler: %w", err)
}
@ -77,19 +86,45 @@ func (c *Client) StartFlushScheduler(config *csconfig.FlushDBCfg) (*gocron.Sched
log.Warning("bouncers auto-delete for login/password auth is not supported (use cert or api)")
}
}
baJob, err := scheduler.Every(1).Minute().Do(c.FlushAgentsAndBouncers, config.AgentsGC, config.BouncersGC)
baJob, err := scheduler.Every(flushInterval).Do(c.FlushAgentsAndBouncers, config.AgentsGC, config.BouncersGC)
if err != nil {
return nil, fmt.Errorf("while starting FlushAgentsAndBouncers scheduler: %w", err)
}
baJob.SingletonMode()
scheduler.StartAsync()
// TODO: flush metrics here (MetricsMaxAge)
metricsJob, err := scheduler.Every(flushInterval).Do(c.flushMetrics, config.MetricsMaxAge)
if err != nil {
return nil, fmt.Errorf("while starting flushMetrics scheduler: %w", err)
}
metricsJob.SingletonMode()
scheduler.StartAsync()
return scheduler, nil
}
// flushMetrics deletes metrics older than maxAge, regardless if they have been pushed to CAPI or not
func (c *Client) flushMetrics(maxAge *time.Duration) {
if maxAge == nil {
maxAge = ptr.Of(defaultMetricsMaxAge)
}
c.Log.Debugf("flushing metrics older than %s", maxAge)
deleted, err := c.Ent.Metric.Delete().Where(
metric.CollectedAtLTE(time.Now().UTC().Add(-*maxAge)),
).Exec(c.CTX)
if err != nil {
c.Log.Errorf("while flushing metrics: %s", err)
return
}
if deleted > 0 {
c.Log.Debugf("flushed %d metrics snapshots", deleted)
}
}
func (c *Client) FlushOrphans() {
/* While it has only been linked to some very corner-case bug : https://github.com/crowdsecurity/crowdsec/issues/778 */

View file

@ -1,10 +1,9 @@
package database
import (
"fmt"
"time"
"github.com/pkg/errors"
"github.com/crowdsecurity/crowdsec/pkg/database/ent"
"github.com/crowdsecurity/crowdsec/pkg/database/ent/metric"
)
@ -26,9 +25,15 @@ func (c *Client) CreateMetric(generatedType metric.GeneratedType, generatedBy st
SetPayload(payload).
Save(c.CTX)
if err != nil {
switch {
case ent.IsConstraintError(err):
// pretty safe guess, it's the unique index
c.Log.Infof("storing metrics snapshot for '%s' at %s: already exists", generatedBy, collectedAt)
// it's polite to accept a duplicate snapshot without any error
return nil, nil
case err != nil:
c.Log.Warningf("CreateMetric: %s", err)
return nil, errors.Wrapf(InsertFail, "creating metrics set for '%s' at %s", generatedBy, collectedAt)
return nil, fmt.Errorf("storing metrics snapshot for '%s' at %s: %w", generatedBy, collectedAt, InsertFail)
}
return metric, nil