feat: add option for materialized types & improve filter performance for materialized Prefixes

This commit is contained in:
Marvin Gaube
2023-09-08 15:09:30 +02:00
committed by Vincent Bernat
parent 41932ca836
commit 5efa368e79
6 changed files with 82 additions and 15 deletions

View File

@@ -159,9 +159,10 @@ func flows() Schema {
ConsoleNotDimension: true, ConsoleNotDimension: true,
}, },
{ {
Key: ColumnSrcNetPrefix, Key: ColumnSrcNetPrefix,
ClickHouseMainOnly: true, ClickHouseMainOnly: true,
ClickHouseType: "String", ClickHouseType: "String",
ClickHouseMaterializedType: "LowCardinality(String)",
ClickHouseAlias: `CASE ClickHouseAlias: `CASE
WHEN EType = 0x800 THEN concat(replaceRegexpOne(IPv6CIDRToRange(SrcAddr, (96 + SrcNetMask)::UInt8).1::String, '^::ffff:', ''), '/', SrcNetMask::String) WHEN EType = 0x800 THEN concat(replaceRegexpOne(IPv6CIDRToRange(SrcAddr, (96 + SrcNetMask)::UInt8).1::String, '^::ffff:', ''), '/', SrcNetMask::String)
WHEN EType = 0x86dd THEN concat(IPv6CIDRToRange(SrcAddr, SrcNetMask).1::String, '/', SrcNetMask::String) WHEN EType = 0x86dd THEN concat(IPv6CIDRToRange(SrcAddr, SrcNetMask).1::String, '/', SrcNetMask::String)

View File

@@ -30,9 +30,15 @@ func New(config Configuration) (*Component, error) {
if column.ClickHouseAlias != "" { if column.ClickHouseAlias != "" {
column.ClickHouseGenerateFrom = column.ClickHouseAlias column.ClickHouseGenerateFrom = column.ClickHouseAlias
column.ClickHouseAlias = "" column.ClickHouseAlias = ""
column.ClickHouseMaterialized = true
} else { } else {
return nil, fmt.Errorf("no alias configured for %s that can be converted to generate", k) return nil, fmt.Errorf("no alias configured for %s that can be converted to generate", k)
} }
// in case we have another data type for materialized columns, set it
if column.ClickHouseMaterializedType != "" {
column.ClickHouseType = column.ClickHouseMaterializedType
}
} }
} }
for _, k := range config.Enabled { for _, k := range config.Enabled {

View File

@@ -39,14 +39,18 @@ type Column struct {
// instead of being retrieved from the protobuf. `TransformFrom' and // instead of being retrieved from the protobuf. `TransformFrom' and
// `TransformTo' work in pairs. The first one is the set of column in the // `TransformTo' work in pairs. The first one is the set of column in the
// raw table while the second one is how to transform it for the main table. // raw table while the second one is how to transform it for the main table.
ClickHouseType string ClickHouseType string
ClickHouseCodec string ClickHouseMaterializedType string
ClickHouseAlias string ClickHouseCodec string
ClickHouseNotSortingKey bool ClickHouseAlias string
ClickHouseGenerateFrom string ClickHouseNotSortingKey bool
ClickHouseTransformFrom []Column ClickHouseGenerateFrom string
ClickHouseTransformTo string ClickHouseTransformFrom []Column
ClickHouseMainOnly bool ClickHouseTransformTo string
ClickHouseMainOnly bool
// ClickHouseMaterialized indicates that the column was materialized (and is not by default)
ClickHouseMaterialized bool
// For the console. `ClickHouseTruncateIP' makes the specified column // For the console. `ClickHouseTruncateIP' makes the specified column
// truncatable when used as a dimension. // truncatable when used as a dimension.

View File

@@ -127,11 +127,21 @@ func (c *current) parsePrefix(direction string) ([]any, error) {
if err != nil { if err != nil {
return []any{}, errors.New("expecting a prefix") return []any{}, errors.New("expecting a prefix")
} }
// if the prefix was materialized, we can directly access it
col := c.getColumn(fmt.Sprintf("%sNetPrefix", direction))
if col.ClickHouseMaterialized {
return []any{
fmt.Sprintf("%sNetPrefix", direction), "=",
fmt.Sprintf("'%s'", net.String())}, nil
}
// it the prefix is not materialized, we use the "between" operator
c.globalStore["meta"].(*Meta).MainTableRequired = true
prefix := "::ffff:" prefix := "::ffff:"
if net.Addr().Is6() { if net.Addr().Is6() {
prefix = "" prefix = ""
} }
return []any{ return []any{
fmt.Sprintf("%sAddr", direction),
fmt.Sprintf("BETWEEN toIPv6('%s%s') AND toIPv6('%s%s') AND", fmt.Sprintf("BETWEEN toIPv6('%s%s') AND toIPv6('%s%s') AND",
prefix, net.Masked().Addr().String(), prefix, lastIP(net).String()), prefix, net.Masked().Addr().String(), prefix, lastIP(net).String()),
c.getColumn(fmt.Sprintf("%sNetMask", direction)), "=", net.Bits(), c.getColumn(fmt.Sprintf("%sNetMask", direction)), "=", net.Bits(),

View File

@@ -78,8 +78,8 @@ ConditionPrefixExpr "condition on prefix" ←
operator:("=" / "!=") _ operator:("=" / "!=") _
prefix:SourcePrefix { prefix:SourcePrefix {
switch toString(operator) { switch toString(operator) {
case "=": return []any{c.getColumn("SrcAddr"), prefix}, nil case "=": return []any{prefix}, nil
case "!=": return []any{"NOT (", c.getColumn("SrcAddr"), prefix, ")"}, nil case "!=": return []any{"NOT (", prefix, ")"}, nil
} }
return "", nil return "", nil
} }
@@ -87,8 +87,8 @@ ConditionPrefixExpr "condition on prefix" ←
operator:("=" / "!=") _ operator:("=" / "!=") _
prefix:DestinationPrefix { prefix:DestinationPrefix {
switch toString(operator) { switch toString(operator) {
case "=": return []any{c.getColumn("DstAddr"), prefix}, nil case "=": return []any{prefix}, nil
case "!=": return []any{"NOT (", c.getColumn("DstAddr"), prefix, ")"}, nil case "!=": return []any{"NOT (", prefix, ")"}, nil
} }
return "", nil return "", nil
} }

View File

@@ -338,6 +338,52 @@ output provider */ = 'telia'`,
} }
} }
func TestValidMaterializedFilter(t *testing.T) {
cases := []struct {
Input string
Output string
MetaIn Meta
MetaOut Meta
}{
{
Input: `DstNetPrefix = 192.168.0.128/27`,
Output: `DstNetPrefix = '192.168.0.128/27'`,
MetaOut: Meta{MainTableRequired: false},
},
{
Input: `SrcNetPrefix = 192.168.0.128/27`,
Output: `SrcNetPrefix = '192.168.0.128/27'`,
MetaOut: Meta{MainTableRequired: false},
},
{
Input: `SrcNetPrefix = 2001:db8::/48`,
Output: `SrcNetPrefix = '2001:db8::/48'`,
MetaOut: Meta{MainTableRequired: false},
},
}
for _, tc := range cases {
s := schema.NewMock(t).EnableAllColumns()
cd, _ := s.Schema.LookupColumnByKey(schema.ColumnDstNetPrefix)
cd.ClickHouseMaterialized = true
cs, _ := s.Schema.LookupColumnByKey(schema.ColumnSrcNetPrefix)
cs.ClickHouseMaterialized = true
tc.MetaIn.Schema = s
tc.MetaOut.Schema = tc.MetaIn.Schema
got, err := Parse("", []byte(tc.Input), GlobalStore("meta", &tc.MetaIn))
if err != nil {
t.Errorf("Parse(%q) error:\n%+v", tc.Input, err)
continue
}
if diff := helpers.Diff(got.(string), tc.Output); diff != "" {
t.Errorf("Parse(%q) (-got, +want):\n%s", tc.Input, diff)
}
if diff := helpers.Diff(tc.MetaIn, tc.MetaOut); diff != "" {
t.Errorf("Parse(%q) meta (-got, +want):\n%s", tc.Input, diff)
}
}
}
func TestInvalidFilter(t *testing.T) { func TestInvalidFilter(t *testing.T) {
cases := []struct { cases := []struct {
Input string Input string