app/vmstorage: add -denyQueriesOutsideRetention command-line flag for denying queries outside the configured retention

This commit is contained in:
Aliaksandr Valialkin 2020-07-01 00:58:26 +03:00
parent 81e3d4305f
commit 4cb3e7595c
3 changed files with 56 additions and 8 deletions

View file

@ -2,10 +2,13 @@ package netstorage
import ( import (
"container/heap" "container/heap"
"errors"
"fmt" "fmt"
"io" "io"
"net/http"
"runtime" "runtime"
"sort" "sort"
"strings"
"sync" "sync"
"time" "time"
@ -15,6 +18,7 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding" "github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime" "github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/handshake" "github.com/VictoriaMetrics/VictoriaMetrics/lib/handshake"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil" "github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage" "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
@ -1147,7 +1151,8 @@ func (sn *storageNode) execOnConn(rpcName string, f func(bc *handshake.BufferedC
if err := f(bc); err != nil { if err := f(bc); err != nil {
remoteAddr := bc.RemoteAddr() remoteAddr := bc.RemoteAddr()
if _, ok := err.(*errRemote); ok { var er *errRemote
if errors.As(err, &er) {
// Remote error. The connection may be re-used. Return it to the pool. // Remote error. The connection may be re-used. Return it to the pool.
sn.connPool.Put(bc) sn.connPool.Put(bc)
} else { } else {
@ -1171,6 +1176,19 @@ func (er *errRemote) Error() string {
return er.msg return er.msg
} }
func newErrRemote(buf []byte) error {
err := &errRemote{
msg: string(buf),
}
if !strings.Contains(err.msg, "denyQueriesOutsideRetention") {
return err
}
return &httpserver.ErrorWithStatusCode{
Err: err,
StatusCode: http.StatusServiceUnavailable,
}
}
func (sn *storageNode) deleteMetricsOnConn(bc *handshake.BufferedConn, requestData []byte) (int, error) { func (sn *storageNode) deleteMetricsOnConn(bc *handshake.BufferedConn, requestData []byte) (int, error) {
// Send the request to sn // Send the request to sn
if err := writeBytes(bc, requestData); err != nil { if err := writeBytes(bc, requestData); err != nil {
@ -1186,7 +1204,7 @@ func (sn *storageNode) deleteMetricsOnConn(bc *handshake.BufferedConn, requestDa
return 0, fmt.Errorf("cannot read error message: %w", err) return 0, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return 0, &errRemote{msg: string(buf)} return 0, newErrRemote(buf)
} }
// Read deletedCount // Read deletedCount
@ -1217,7 +1235,7 @@ func (sn *storageNode) getLabelsOnConn(bc *handshake.BufferedConn, accountID, pr
return nil, fmt.Errorf("cannot read error message: %w", err) return nil, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return nil, &errRemote{msg: string(buf)} return nil, newErrRemote(buf)
} }
// Read response // Read response
@ -1258,7 +1276,7 @@ func (sn *storageNode) getLabelValuesOnConn(bc *handshake.BufferedConn, accountI
return nil, fmt.Errorf("cannot read error message: %w", err) return nil, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return nil, &errRemote{msg: string(buf)} return nil, newErrRemote(buf)
} }
// Read response // Read response
@ -1303,7 +1321,7 @@ func (sn *storageNode) getLabelEntriesOnConn(bc *handshake.BufferedConn, account
return nil, fmt.Errorf("cannot read error message: %w", err) return nil, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return nil, &errRemote{msg: string(buf)} return nil, newErrRemote(buf)
} }
// Read response // Read response
@ -1356,7 +1374,7 @@ func (sn *storageNode) getTSDBStatusForDateOnConn(bc *handshake.BufferedConn, ac
return nil, fmt.Errorf("cannot read error message: %w", err) return nil, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return nil, &errRemote{msg: string(buf)} return nil, newErrRemote(buf)
} }
// Read response // Read response
@ -1422,7 +1440,7 @@ func (sn *storageNode) getSeriesCountOnConn(bc *handshake.BufferedConn, accountI
return 0, fmt.Errorf("cannot read error message: %w", err) return 0, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return 0, &errRemote{msg: string(buf)} return 0, newErrRemote(buf)
} }
// Read response // Read response
@ -1461,7 +1479,7 @@ func (sn *storageNode) processSearchQueryOnConn(tbfw *tmpBlocksFileWrapper, bc *
return 0, fmt.Errorf("cannot read error message: %w", err) return 0, fmt.Errorf("cannot read error message: %w", err)
} }
if len(buf) > 0 { if len(buf) > 0 {
return 0, &errRemote{msg: string(buf)} return 0, newErrRemote(buf)
} }
// Read response. It may consist of multiple MetricBlocks. // Read response. It may consist of multiple MetricBlocks.

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"net/http"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -14,6 +15,7 @@ import (
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding" "github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime" "github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/handshake" "github.com/VictoriaMetrics/VictoriaMetrics/lib/handshake"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil" "github.com/VictoriaMetrics/VictoriaMetrics/lib/netutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage" "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
@ -27,6 +29,10 @@ var (
precisionBits = flag.Int("precisionBits", 64, "The number of precision bits to store per each value. Lower precision bits improves data compression at the cost of precision loss") precisionBits = flag.Int("precisionBits", 64, "The number of precision bits to store per each value. Lower precision bits improves data compression at the cost of precision loss")
disableRPCCompression = flag.Bool(`rpc.disableCompression`, false, "Disable compression of RPC traffic. This reduces CPU usage at the cost of higher network bandwidth usage") disableRPCCompression = flag.Bool(`rpc.disableCompression`, false, "Disable compression of RPC traffic. This reduces CPU usage at the cost of higher network bandwidth usage")
denyQueriesOutsideRetention = flag.Bool("denyQueriesOutsideRetention", false, "Whether to deny queries outside of the configured -retentionPeriod. "+
"When set, then /api/v1/query_range would return '503 Service Unavailable' error for queries with 'from' value outside -retentionPeriod. "+
"This may be useful when multiple data sources with distinct retentions are hidden behind query-tee")
) )
// Server processes connections from vminsert and vmselect. // Server processes connections from vminsert and vmselect.
@ -824,6 +830,9 @@ func (s *Server) processVMSelectSearchQuery(ctx *vmselectRequestCtx) error {
MinTimestamp: ctx.sq.MinTimestamp, MinTimestamp: ctx.sq.MinTimestamp,
MaxTimestamp: ctx.sq.MaxTimestamp, MaxTimestamp: ctx.sq.MaxTimestamp,
} }
if err := checkTimeRange(s.storage, tr); err != nil {
return ctx.writeErrorMessage(err)
}
ctx.sr.Init(s.storage, ctx.tfss, tr, *maxMetricsPerSearch) ctx.sr.Init(s.storage, ctx.tfss, tr, *maxMetricsPerSearch)
defer ctx.sr.MustClose() defer ctx.sr.MustClose()
if err := ctx.sr.Error(); err != nil { if err := ctx.sr.Error(); err != nil {
@ -859,6 +868,22 @@ func (s *Server) processVMSelectSearchQuery(ctx *vmselectRequestCtx) error {
return nil return nil
} }
// checkTimeRange returns true if the given tr is denied for querying.
func checkTimeRange(s *storage.Storage, tr storage.TimeRange) error {
if !*denyQueriesOutsideRetention {
return nil
}
retentionPeriod := s.RetentionMonths()
minAllowedTimestamp := (int64(fasttime.UnixTimestamp()) - int64(retentionPeriod)*3600*24*30) * 1000
if tr.MinTimestamp > minAllowedTimestamp {
return nil
}
return &httpserver.ErrorWithStatusCode{
Err: fmt.Errorf("the given time range %s is outside the allowed retention of %d months according to -denyQueriesOutsideRetention", &tr, retentionPeriod),
StatusCode: http.StatusServiceUnavailable,
}
}
var ( var (
vmselectDeleteMetricsRequests = metrics.NewCounter("vm_vmselect_delete_metrics_requests_total") vmselectDeleteMetricsRequests = metrics.NewCounter("vm_vmselect_delete_metrics_requests_total")
vmselectLabelsRequests = metrics.NewCounter("vm_vmselect_labels_requests_total") vmselectLabelsRequests = metrics.NewCounter("vm_vmselect_labels_requests_total")

View file

@ -200,6 +200,11 @@ func OpenStorage(path string, retentionMonths int) (*Storage, error) {
return s, nil return s, nil
} }
// RetentionMonths returns retention months for s.
func (s *Storage) RetentionMonths() int {
return s.retentionMonths
}
// debugFlush flushes recently added storage data, so it becomes visible to search. // debugFlush flushes recently added storage data, so it becomes visible to search.
func (s *Storage) debugFlush() { func (s *Storage) debugFlush() {
s.tb.flushRawRows() s.tb.flushRawRows()