@ -7,6 +7,7 @@ import (
"time"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/labels"
"github.com/stretchr/testify/require"
"github.com/grafana/loki/pkg/logql/syntax"
@ -27,6 +28,8 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
lblFoo , err := syntax . ParseLabels ( ` { foo="bar"} ` )
require . NoError ( t , err )
streamSelectorWithLineFilters := lblFoo . String ( ) + ` |="fizz" `
streamSelectorWithNonIndexedLabelsFilters := lblFoo . String ( ) + ` | ping="pong" `
streamSelectorWithLineAndNonIndexedLabelsFilters := lblFoo . String ( ) + ` | ping="pong" |= "fizz" `
chunkEntry := retention . ChunkEntry {
ChunkRef : retention . ChunkRef {
@ -75,6 +78,50 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
} ,
} ,
{
name : "no relevant delete requests" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : "different-user" ,
Query : lblFoo . String ( ) ,
StartTime : now . Add ( - 24 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : false ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
"different-user" : {
Start : now . Add ( - 24 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "delete request not matching labels" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : testUserID ,
Query : ` { fizz="buzz"} ` ,
StartTime : now . Add ( - 24 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : false ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
testUserID : {
Start : now . Add ( - 24 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "whole chunk deleted by single request" ,
deletionMode : deletionmode . FilterAndDelete ,
@ -111,7 +158,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
return strings . Contains ( s , "fizz" )
} ,
} ,
@ -122,6 +169,56 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
} ,
} ,
{
name : "whole chunk deleted by single request with non-indexed labels filters" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 24 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string , nonIndexedLabels ... labels . Label ) bool {
return labels . Labels ( nonIndexedLabels ) . Get ( lblPing ) == lblPong
} ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
testUserID : {
Start : now . Add ( - 24 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "whole chunk deleted by single request with line and non-indexed labels filters" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : testUserID ,
Query : streamSelectorWithLineAndNonIndexedLabelsFilters ,
StartTime : now . Add ( - 24 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string , nonIndexedLabels ... labels . Label ) bool {
return labels . Labels ( nonIndexedLabels ) . Get ( lblPing ) == lblPong && strings . Contains ( s , "fizz" )
} ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
testUserID : {
Start : now . Add ( - 24 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "deleted interval out of range" ,
deletionMode : deletionmode . FilterAndDelete ,
@ -224,7 +321,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
return strings . Contains ( s , "fizz" )
} ,
} ,
@ -235,6 +332,37 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
} ,
} ,
{
name : "multiple delete requests with non-indexed labels filters and one deleting the whole chunk" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 48 * time . Hour ) ,
EndTime : now . Add ( - 24 * time . Hour ) ,
} ,
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 12 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string , nonIndexedLabels ... labels . Label ) bool {
return labels . Labels ( nonIndexedLabels ) . Get ( lblPing ) == lblPong
} ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
testUserID : {
Start : now . Add ( - 48 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "multiple delete requests causing multiple holes" ,
deletionMode : deletionmode . FilterAndDelete ,
@ -267,7 +395,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
tsUnixNano := ts . UnixNano ( )
if ( now . Add ( - 13 * time . Hour ) . UnixNano ( ) <= tsUnixNano && tsUnixNano <= now . Add ( - 11 * time . Hour ) . UnixNano ( ) ) ||
( now . Add ( - 10 * time . Hour ) . UnixNano ( ) <= tsUnixNano && tsUnixNano <= now . Add ( - 8 * time . Hour ) . UnixNano ( ) ) ||
@ -305,7 +433,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
return true
} ,
} ,
@ -336,7 +464,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
return strings . Contains ( s , "fizz" )
} ,
} ,
@ -347,6 +475,37 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
} ,
} ,
{
name : "multiple overlapping requests with non-indexed labels filters deleting the whole chunk" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 13 * time . Hour ) ,
EndTime : now . Add ( - 6 * time . Hour ) ,
} ,
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 8 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string , nonIndexedLabels ... labels . Label ) bool {
return labels . Labels ( nonIndexedLabels ) . Get ( lblPing ) == lblPong
} ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
testUserID : {
Start : now . Add ( - 13 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "multiple non-overlapping requests deleting the whole chunk" ,
deletionMode : deletionmode . FilterAndDelete ,
@ -373,7 +532,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
return true
} ,
} ,
@ -410,7 +569,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
return strings . Contains ( s , "fizz" )
} ,
} ,
@ -421,6 +580,43 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
} ,
} ,
{
name : "multiple non-overlapping requests with non-indexed labels filter deleting the whole chunk" ,
deletionMode : deletionmode . FilterAndDelete ,
batchSize : 70 ,
deleteRequestsFromStore : [ ] DeleteRequest {
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 12 * time . Hour ) ,
EndTime : now . Add ( - 6 * time . Hour ) - 1 ,
} ,
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 6 * time . Hour ) ,
EndTime : now . Add ( - 4 * time . Hour ) - 1 ,
} ,
{
UserID : testUserID ,
Query : streamSelectorWithNonIndexedLabelsFilters ,
StartTime : now . Add ( - 4 * time . Hour ) ,
EndTime : now ,
} ,
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string , nonIndexedLabels ... labels . Label ) bool {
return labels . Labels ( nonIndexedLabels ) . Get ( lblPing ) == lblPong
} ,
} ,
expectedDeletionRangeByUser : map [ string ] model . Interval {
testUserID : {
Start : now . Add ( - 12 * time . Hour ) ,
End : now ,
} ,
} ,
} ,
{
name : "deletes are disabled" ,
deletionMode : deletionmode . Disabled ,
@ -521,7 +717,7 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
} ,
expectedResp : resp {
isExpired : true ,
expectedFilter : func ( ts time . Time , s string ) bool {
expectedFilter : func ( ts time . Time , s string , _ ... labels . Label ) bool {
tsUnixNano := ts . UnixNano ( )
if ( now . Add ( - 13 * time . Hour ) . UnixNano ( ) <= tsUnixNano && tsUnixNano <= now . Add ( - 11 * time . Hour ) . UnixNano ( ) ) ||
( now . Add ( - 10 * time . Hour ) . UnixNano ( ) <= tsUnixNano && tsUnixNano <= now . Add ( - 8 * time . Hour ) . UnixNano ( ) ) {
@ -562,7 +758,14 @@ func TestDeleteRequestsManager_Expired(t *testing.T) {
if start . Time ( ) . Minute ( ) % 2 == 1 {
line = "fizz buzz"
}
require . Equal ( t , tc . expectedResp . expectedFilter ( start . Time ( ) , line ) , filterFunc ( start . Time ( ) , line ) , "line" , line , "time" , start . Time ( ) , "now" , now . Time ( ) )
// mix of empty, ding=dong and ping=pong as non-indexed labels
var nonIndexedLabels [ ] labels . Label
if start . Time ( ) . Minute ( ) % 3 == 0 {
nonIndexedLabels = [ ] labels . Label { { Name : lblPing , Value : lblPong } }
} else if start . Time ( ) . Minute ( ) % 2 == 0 {
nonIndexedLabels = [ ] labels . Label { { Name : "ting" , Value : "tong" } }
}
require . Equal ( t , tc . expectedResp . expectedFilter ( start . Time ( ) , line , nonIndexedLabels ... ) , filterFunc ( start . Time ( ) , line , nonIndexedLabels ... ) , "line" , line , "time" , start . Time ( ) , "now" , now . Time ( ) )
}
require . Equal ( t , len ( tc . expectedDeletionRangeByUser ) , len ( mgr . deleteRequestsToProcess ) )