Skip to content

Commit f3d13d4

Browse files
OleksiienkoMykytajoao-r-reis
authored andcommitted
Add support for cassandra 4.0 table options
In the PR implemented backward compatibility with previous versions, and added new types support. To make metadata table support easier for future Cassandra versions, hardcode scan from Cassandra were replaced with new "parseSystemSchemaViews" method which is much easier to expand, even if some fields were added in the middle of the table it wouldn`t be an issue anymore. patch by Mykyta Oleksiienko; reviewed by Joao Reis and James Harting CASSGO-13
1 parent 4ad7479 commit f3d13d4

File tree

3 files changed

+208
-57
lines changed

3 files changed

+208
-57
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121

2222
- Change Batch API to be consistent with Query() (CASSGO-7)
2323

24+
- Added Cassandra 4.0 table options support(CASSGO-13)
25+
2426
- Remove deprecated global logger (CASSGO-24)
2527

2628
- Bumped actions/upload-artifact and actions/cache versions to v4 in CI workflow (CASSGO-48)

cassandra_test.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,7 +2328,7 @@ func TestGetColumnMetadata(t *testing.T) {
23282328

23292329
func TestMaterializedViewMetadata(t *testing.T) {
23302330
if flagCassVersion.Before(3, 0, 0) {
2331-
return
2331+
t.Skip("The Cassandra version is too old")
23322332
}
23332333
session := createSession(t)
23342334
defer session.Close()
@@ -2347,14 +2347,19 @@ func TestMaterializedViewMetadata(t *testing.T) {
23472347
expectedChunkLengthInKB := "16"
23482348
expectedDCLocalReadRepairChance := float64(0)
23492349
expectedSpeculativeRetry := "99p"
2350+
expectedAdditionalWritePolicy := "99p"
2351+
expectedReadRepair := "BLOCKING"
23502352
if flagCassVersion.Before(4, 0, 0) {
23512353
expectedChunkLengthInKB = "64"
23522354
expectedDCLocalReadRepairChance = 0.1
23532355
expectedSpeculativeRetry = "99PERCENTILE"
2356+
expectedReadRepair = ""
2357+
expectedAdditionalWritePolicy = ""
23542358
}
23552359
expectedView1 := MaterializedViewMetadata{
23562360
Keyspace: "gocql_test",
23572361
Name: "view_view",
2362+
AdditionalWritePolicy: expectedAdditionalWritePolicy,
23582363
baseTableName: "view_table",
23592364
BloomFilterFpChance: 0.01,
23602365
Caching: map[string]string{"keys": "ALL", "rows_per_partition": "NONE"},
@@ -2366,12 +2371,17 @@ func TestMaterializedViewMetadata(t *testing.T) {
23662371
DefaultTimeToLive: 0,
23672372
Extensions: map[string]string{},
23682373
GcGraceSeconds: 864000,
2369-
IncludeAllColumns: false, MaxIndexInterval: 2048, MemtableFlushPeriodInMs: 0, MinIndexInterval: 128, ReadRepairChance: 0,
2370-
SpeculativeRetry: expectedSpeculativeRetry,
2374+
IncludeAllColumns: false, MaxIndexInterval: 2048,
2375+
MemtableFlushPeriodInMs: 0,
2376+
MinIndexInterval: 128,
2377+
ReadRepair: expectedReadRepair,
2378+
ReadRepairChance: 0,
2379+
SpeculativeRetry: expectedSpeculativeRetry,
23712380
}
23722381
expectedView2 := MaterializedViewMetadata{
23732382
Keyspace: "gocql_test",
23742383
Name: "view_view2",
2384+
AdditionalWritePolicy: expectedAdditionalWritePolicy,
23752385
baseTableName: "view_table2",
23762386
BloomFilterFpChance: 0.01,
23772387
Caching: map[string]string{"keys": "ALL", "rows_per_partition": "NONE"},
@@ -2383,8 +2393,13 @@ func TestMaterializedViewMetadata(t *testing.T) {
23832393
DefaultTimeToLive: 0,
23842394
Extensions: map[string]string{},
23852395
GcGraceSeconds: 864000,
2386-
IncludeAllColumns: false, MaxIndexInterval: 2048, MemtableFlushPeriodInMs: 0, MinIndexInterval: 128, ReadRepairChance: 0,
2387-
SpeculativeRetry: expectedSpeculativeRetry,
2396+
IncludeAllColumns: false,
2397+
MaxIndexInterval: 2048,
2398+
MemtableFlushPeriodInMs: 0,
2399+
MinIndexInterval: 128,
2400+
ReadRepair: expectedReadRepair,
2401+
ReadRepairChance: 0,
2402+
SpeculativeRetry: expectedSpeculativeRetry,
23882403
}
23892404

23902405
expectedView1.BaseTableId = materializedViews[0].BaseTableId

metadata.go

Lines changed: 186 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ type AggregateMetadata struct {
111111
type MaterializedViewMetadata struct {
112112
Keyspace string
113113
Name string
114+
AdditionalWritePolicy string
114115
BaseTableId UUID
115116
BaseTable *TableMetadata
116117
BloomFilterFpChance float64
@@ -128,7 +129,8 @@ type MaterializedViewMetadata struct {
128129
MaxIndexInterval int
129130
MemtableFlushPeriodInMs int
130131
MinIndexInterval int
131-
ReadRepairChance float64
132+
ReadRepair string // Only present in Cassandra 4.0+
133+
ReadRepairChance float64 // Note: Cassandra 4.0 removed ReadRepairChance and added ReadRepair instead
132134
SpeculativeRetry string
133135

134136
baseTableName string
@@ -975,69 +977,201 @@ func getUserTypeMetadata(session *Session, keyspaceName string) ([]UserTypeMetad
975977
return uTypes, nil
976978
}
977979

980+
func bytesMapToStringsMap(byteData map[string][]byte) map[string]string {
981+
extensions := make(map[string]string, len(byteData))
982+
for key, rowByte := range byteData {
983+
extensions[key] = string(rowByte)
984+
}
985+
986+
return extensions
987+
}
988+
989+
func materializedViewMetadataFromMap(currentObject map[string]interface{}, materializedView *MaterializedViewMetadata) error {
990+
const errorMessage = "gocql.materializedViewMetadataFromMap failed to read column %s"
991+
var ok bool
992+
for key, value := range currentObject {
993+
switch key {
994+
case "keyspace_name":
995+
materializedView.Keyspace, ok = value.(string)
996+
if !ok {
997+
return fmt.Errorf(errorMessage, key)
998+
}
999+
1000+
case "view_name":
1001+
materializedView.Name, ok = value.(string)
1002+
if !ok {
1003+
return fmt.Errorf(errorMessage, key)
1004+
}
1005+
1006+
case "additional_write_policy":
1007+
materializedView.AdditionalWritePolicy, ok = value.(string)
1008+
if !ok {
1009+
return fmt.Errorf(errorMessage, key)
1010+
}
1011+
1012+
case "base_table_id":
1013+
materializedView.BaseTableId, ok = value.(UUID)
1014+
if !ok {
1015+
return fmt.Errorf(errorMessage, key)
1016+
}
1017+
1018+
case "base_table_name":
1019+
materializedView.baseTableName, ok = value.(string)
1020+
if !ok {
1021+
return fmt.Errorf(errorMessage, key)
1022+
}
1023+
1024+
case "bloom_filter_fp_chance":
1025+
materializedView.BloomFilterFpChance, ok = value.(float64)
1026+
if !ok {
1027+
return fmt.Errorf(errorMessage, key)
1028+
}
1029+
1030+
case "caching":
1031+
materializedView.Caching, ok = value.(map[string]string)
1032+
if !ok {
1033+
return fmt.Errorf(errorMessage, key)
1034+
}
1035+
1036+
case "comment":
1037+
materializedView.Comment, ok = value.(string)
1038+
if !ok {
1039+
return fmt.Errorf(errorMessage, key)
1040+
}
1041+
1042+
case "compaction":
1043+
materializedView.Compaction, ok = value.(map[string]string)
1044+
if !ok {
1045+
return fmt.Errorf(errorMessage, key)
1046+
}
1047+
1048+
case "compression":
1049+
materializedView.Compression, ok = value.(map[string]string)
1050+
if !ok {
1051+
return fmt.Errorf(errorMessage, key)
1052+
}
1053+
1054+
case "crc_check_chance":
1055+
materializedView.CrcCheckChance, ok = value.(float64)
1056+
if !ok {
1057+
return fmt.Errorf(errorMessage, key)
1058+
}
1059+
1060+
case "dclocal_read_repair_chance":
1061+
materializedView.DcLocalReadRepairChance, ok = value.(float64)
1062+
if !ok {
1063+
return fmt.Errorf(errorMessage, key)
1064+
}
1065+
1066+
case "default_time_to_live":
1067+
materializedView.DefaultTimeToLive, ok = value.(int)
1068+
if !ok {
1069+
return fmt.Errorf(errorMessage, key)
1070+
}
1071+
1072+
case "extensions":
1073+
byteData, ok := value.(map[string][]byte)
1074+
if !ok {
1075+
return fmt.Errorf(errorMessage, key)
1076+
}
1077+
1078+
materializedView.Extensions = bytesMapToStringsMap(byteData)
1079+
1080+
case "gc_grace_seconds":
1081+
materializedView.GcGraceSeconds, ok = value.(int)
1082+
if !ok {
1083+
return fmt.Errorf(errorMessage, key)
1084+
}
1085+
1086+
case "id":
1087+
materializedView.Id, ok = value.(UUID)
1088+
if !ok {
1089+
return fmt.Errorf(errorMessage, key)
1090+
}
1091+
1092+
case "include_all_columns":
1093+
materializedView.IncludeAllColumns, ok = value.(bool)
1094+
if !ok {
1095+
return fmt.Errorf(errorMessage, key)
1096+
}
1097+
1098+
case "max_index_interval":
1099+
materializedView.MaxIndexInterval, ok = value.(int)
1100+
if !ok {
1101+
return fmt.Errorf(errorMessage, key)
1102+
}
1103+
1104+
case "memtable_flush_period_in_ms":
1105+
materializedView.MemtableFlushPeriodInMs, ok = value.(int)
1106+
if !ok {
1107+
return fmt.Errorf(errorMessage, key)
1108+
}
1109+
1110+
case "min_index_interval":
1111+
materializedView.MinIndexInterval, ok = value.(int)
1112+
if !ok {
1113+
return fmt.Errorf(errorMessage, key)
1114+
}
1115+
1116+
case "read_repair":
1117+
materializedView.ReadRepair, ok = value.(string)
1118+
if !ok {
1119+
return fmt.Errorf(errorMessage, key)
1120+
}
1121+
1122+
case "read_repair_chance":
1123+
materializedView.ReadRepairChance, ok = value.(float64)
1124+
if !ok {
1125+
return fmt.Errorf(errorMessage, key)
1126+
}
1127+
1128+
case "speculative_retry":
1129+
materializedView.SpeculativeRetry, ok = value.(string)
1130+
if !ok {
1131+
return fmt.Errorf(errorMessage, key)
1132+
}
1133+
1134+
}
1135+
}
1136+
return nil
1137+
}
1138+
1139+
func parseSystemSchemaViews(iter *Iter) ([]MaterializedViewMetadata, error) {
1140+
var materializedViews []MaterializedViewMetadata
1141+
s, err := iter.SliceMap()
1142+
if err != nil {
1143+
return nil, err
1144+
}
1145+
1146+
for _, row := range s {
1147+
var materializedView MaterializedViewMetadata
1148+
err = materializedViewMetadataFromMap(row, &materializedView)
1149+
if err != nil {
1150+
return nil, err
1151+
}
1152+
1153+
materializedViews = append(materializedViews, materializedView)
1154+
}
1155+
1156+
return materializedViews, nil
1157+
}
1158+
9781159
func getMaterializedViewsMetadata(session *Session, keyspaceName string) ([]MaterializedViewMetadata, error) {
9791160
if !session.useSystemSchema {
9801161
return nil, nil
9811162
}
9821163
var tableName = "system_schema.views"
9831164
stmt := fmt.Sprintf(`
984-
SELECT
985-
view_name,
986-
base_table_id,
987-
base_table_name,
988-
bloom_filter_fp_chance,
989-
caching,
990-
comment,
991-
compaction,
992-
compression,
993-
crc_check_chance,
994-
dclocal_read_repair_chance,
995-
default_time_to_live,
996-
extensions,
997-
gc_grace_seconds,
998-
id,
999-
include_all_columns,
1000-
max_index_interval,
1001-
memtable_flush_period_in_ms,
1002-
min_index_interval,
1003-
read_repair_chance,
1004-
speculative_retry
1165+
SELECT *
10051166
FROM %s
10061167
WHERE keyspace_name = ?`, tableName)
10071168

10081169
var materializedViews []MaterializedViewMetadata
10091170

1010-
rows := session.control.query(stmt, keyspaceName).Scanner()
1011-
for rows.Next() {
1012-
materializedView := MaterializedViewMetadata{Keyspace: keyspaceName}
1013-
err := rows.Scan(&materializedView.Name,
1014-
&materializedView.BaseTableId,
1015-
&materializedView.baseTableName,
1016-
&materializedView.BloomFilterFpChance,
1017-
&materializedView.Caching,
1018-
&materializedView.Comment,
1019-
&materializedView.Compaction,
1020-
&materializedView.Compression,
1021-
&materializedView.CrcCheckChance,
1022-
&materializedView.DcLocalReadRepairChance,
1023-
&materializedView.DefaultTimeToLive,
1024-
&materializedView.Extensions,
1025-
&materializedView.GcGraceSeconds,
1026-
&materializedView.Id,
1027-
&materializedView.IncludeAllColumns,
1028-
&materializedView.MaxIndexInterval,
1029-
&materializedView.MemtableFlushPeriodInMs,
1030-
&materializedView.MinIndexInterval,
1031-
&materializedView.ReadRepairChance,
1032-
&materializedView.SpeculativeRetry,
1033-
)
1034-
if err != nil {
1035-
return nil, err
1036-
}
1037-
materializedViews = append(materializedViews, materializedView)
1038-
}
1171+
iter := session.control.query(stmt, keyspaceName)
10391172

1040-
if err := rows.Err(); err != nil {
1173+
materializedViews, err := parseSystemSchemaViews(iter)
1174+
if err != nil {
10411175
return nil, err
10421176
}
10431177

0 commit comments

Comments
 (0)