Skip to content

Commit 29061fd

Browse files
authored
Use Protocol buffers for Apache Arrow Flight testers (#50)
* Remove functionality related to collect metrics * Add protobuf definition used in Apache Flight Interface * Add compiled protobuf file for Python * Add Python stub file to allow syntax highlighting * Use protocol buffer in initialize_database() * Use protocol buffers in register_node() * Use protocol buffers in remove_node() * Use protocol buffers for get_configuration() * Simplify interface for register_node() * Use protocol buffers in update_configuration() * Remove deprecated method to encode arguments * Remove unsued import * Add back original copyright header
1 parent 7242081 commit 29061fd

6 files changed

Lines changed: 385 additions & 68 deletions

File tree

Apache-Arrow-Flight-Tester/common.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ def list_table_names(self) -> list[str]:
4949
flights = self.list_flights()
5050
return [table_name.decode("utf-8") for table_name in flights[0].descriptor.path]
5151

52-
def create_table(
53-
self, table_name: str, columns: list[tuple[str, str]], time_series_table=False
54-
) -> None:
52+
def create_table(self, table_name: str, columns: list[tuple[str, str]], time_series_table=False) -> None:
5553
"""
5654
Create a table in the server or manager with the given name and columns. Each pair in columns should have the
5755
format (column_name, column_type).
@@ -102,9 +100,7 @@ def truncate_table(self, table_name: str) -> None:
102100
"""Truncate the table with the given name in the server or manager."""
103101
self.do_get(Ticket(f"TRUNCATE TABLE {table_name}"))
104102

105-
def clean_up_tables(
106-
self, tables: list[str], operation: Literal["drop", "truncate"]
107-
) -> None:
103+
def clean_up_tables(self, tables: list[str], operation: Literal["drop", "truncate"]) -> None:
108104
"""
109105
Clean up the given tables by either dropping them or truncating them. If no tables are given, all tables
110106
are dropped or truncated.
@@ -125,11 +121,3 @@ def node_type(self) -> str:
125121
"""Return the type of the node."""
126122
node_type = self.do_action("NodeType", b"")
127123
return node_type[0].body.to_pybytes().decode("utf-8")
128-
129-
130-
def encode_argument(argument: str) -> bytes:
131-
"""Encode the given argument as bytes and prepend the size of the argument as a 2-byte integer."""
132-
argument_bytes = str.encode(argument)
133-
argument_size = len(argument_bytes).to_bytes(2, byteorder="big")
134-
135-
return argument_size + argument_bytes

Apache-Arrow-Flight-Tester/manager.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,49 @@
1-
from typing import Literal
2-
31
from pyarrow import flight
42
from pyarrow._flight import Result
53

6-
from common import ModelarDBFlightClient, encode_argument
4+
from common import ModelarDBFlightClient
5+
from protobuf import protocol_pb2
76
from server import ModelarDBServerFlightClient
87

98

109
class ModelarDBManagerFlightClient(ModelarDBFlightClient):
1110
"""Functionality for interacting with a ModelarDB manager using Apache Arrow Flight."""
1211

13-
def initialize_database(self, existing_tables: list[str]) -> list[str]:
12+
def initialize_database(self, existing_tables: list[str]) -> protocol_pb2.TableMetadata:
1413
"""
15-
Retrieve the SQL statements required to initialize the database with the tables that are not included in the
14+
Retrieve the table metadata required to initialize the database with the tables that are not included in the
1615
given list of tables. Throws an error if a table in the given list does not exist in the database.
1716
"""
18-
result = self.do_action(
19-
"InitializeDatabase", str.encode(",".join(existing_tables))
20-
)[0]
21-
decoded_result = result.body.to_pybytes().decode("utf-8")
17+
database_metadata = protocol_pb2.DatabaseMetadata()
18+
database_metadata.table_names.extend(existing_tables)
19+
20+
response = self.do_action("InitializeDatabase", database_metadata.SerializeToString())
2221

23-
return decoded_result.split(";")
22+
table_metadata = protocol_pb2.TableMetadata()
23+
table_metadata.ParseFromString(response[0].body.to_pybytes())
2424

25-
def register_node(
26-
self, node_url: str, node_mode: Literal["cloud", "edge"]
27-
) -> list[Result]:
25+
return table_metadata
26+
27+
def register_node(self, node_url: str,
28+
node_mode: protocol_pb2.NodeMetadata.ServerMode) -> protocol_pb2.ManagerMetadata:
2829
"""Register a node with the given URL and mode in the manager."""
29-
encoded_node_url = encode_argument(node_url)
30-
encoded_node_mode = encode_argument(node_mode)
30+
node_metadata = protocol_pb2.NodeMetadata()
31+
node_metadata.url = node_url
32+
node_metadata.server_mode = node_mode
33+
34+
response = self.do_action("RegisterNode", node_metadata.SerializeToString())
3135

32-
action_body = encoded_node_url + encoded_node_mode
33-
response = self.do_action("RegisterNode", action_body)
36+
manager_metadata = protocol_pb2.ManagerMetadata()
37+
manager_metadata.ParseFromString(response[0].body.to_pybytes())
3438

35-
return response[0].body.to_pybytes()
39+
return manager_metadata
3640

3741
def remove_node(self, node_url: str) -> list[Result]:
3842
"""Remove the node with the given URL from the manager."""
39-
encoded_node_url = encode_argument(node_url)
43+
node_metadata = protocol_pb2.NodeMetadata()
44+
node_metadata.url = node_url
4045

41-
return self.do_action("RemoveNode", encoded_node_url)
46+
return self.do_action("RemoveNode", node_metadata.SerializeToString())
4247

4348
def query(self, query: str) -> None:
4449
"""
@@ -65,7 +70,7 @@ def query(self, query: str) -> None:
6570

6671
print(manager_client.initialize_database(["test_table_1"]))
6772

68-
print(manager_client.register_node("grpc://127.0.0.1:9999", "edge"))
73+
print(manager_client.register_node("grpc://127.0.0.1:9999", protocol_pb2.NodeMetadata.ServerMode.EDGE))
6974
print(manager_client.remove_node("grpc://127.0.0.1:9999"))
7075

7176
manager_client.query("SELECT * FROM test_time_series_table_1 LIMIT 5")
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/* Copyright 2025 The ModelarDB Contributors
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
syntax = "proto3";
17+
18+
package modelardb.flight.protocol;
19+
20+
// Metadata for the ModelarDB cluster manager, including its unique key and storage configuration.
21+
message ManagerMetadata {
22+
// key used to uniquely identify the cluster manager.
23+
string key = 1;
24+
25+
// Storage configuration used to connect to an S3 object store.
26+
message S3Configuration {
27+
string endpoint = 1;
28+
string bucket_name = 2;
29+
string access_key_id = 3;
30+
string secret_access_key = 4;
31+
}
32+
33+
// Storage configuration used to connect to an Azure Blob Storage object store.
34+
message AzureConfiguration {
35+
string account_name = 1;
36+
string access_key = 2;
37+
string container_name = 3;
38+
}
39+
40+
// Storage configuration used by the cluster manager.
41+
oneof storage_configuration {
42+
S3Configuration s3_configuration = 2;
43+
AzureConfiguration azure_configuration = 3;
44+
}
45+
}
46+
47+
// Metadata for a node in the ModelarDB cluster, including its URL and server mode.
48+
message NodeMetadata {
49+
enum ServerMode {
50+
CLOUD = 0;
51+
EDGE = 1;
52+
}
53+
54+
// gRPC URL of the node.
55+
string url = 1;
56+
57+
// Mode indicating whether the node is a cloud or edge server.
58+
ServerMode server_mode = 2;
59+
}
60+
61+
// Metadata for multiple normal tables and time series tables.
62+
message TableMetadata {
63+
// Metadata for a normal table, including its name and schema.
64+
message NormalTableMetadata {
65+
string name = 1;
66+
bytes schema = 2;
67+
}
68+
69+
// Metadata for a time series table, including its name, schema, error bounds, and generated column expressions.
70+
message TimeSeriesTableMetadata {
71+
message ErrorBound {
72+
enum Type {
73+
ABSOLUTE = 0;
74+
RELATIVE = 1;
75+
}
76+
Type type = 1;
77+
float value = 2;
78+
}
79+
80+
string name = 1;
81+
bytes schema = 2;
82+
repeated ErrorBound error_bounds = 3;
83+
repeated bytes generated_column_expressions = 4;
84+
}
85+
86+
// Normal tables included in the table metadata.
87+
repeated NormalTableMetadata normal_tables = 1;
88+
89+
// Time series tables included in the table metadata.
90+
repeated TimeSeriesTableMetadata time_series_tables = 2;
91+
}
92+
93+
// Configuration of a ModelarDB node.
94+
message Configuration {
95+
// Amount of memory to reserve for storing multivariate time series.
96+
uint64 multivariate_reserved_memory_in_bytes = 1;
97+
98+
// Amount of memory to reserve for storing uncompressed data buffers.
99+
uint64 uncompressed_reserved_memory_in_bytes = 2;
100+
101+
// Amount of memory to reserve for storing compressed data buffers.
102+
uint64 compressed_reserved_memory_in_bytes = 3;
103+
104+
// The number of bytes that are required before transferring a batch of data to the remote object store.
105+
optional uint64 transfer_batch_size_in_bytes = 4;
106+
107+
// The number of seconds between each transfer of data to the remote object store.
108+
optional uint64 transfer_time_in_seconds = 5;
109+
110+
// Number of threads to allocate for converting multivariate time series to univariate time series.
111+
uint32 ingestion_threads = 6;
112+
113+
// Number of threads to allocate for compressing univariate time series to segments.
114+
uint32 compression_threads = 7;
115+
116+
// Number of threads to allocate for writing segments to a local and/or remote data folder.
117+
uint32 writer_threads = 8;
118+
}
119+
120+
// Request to update the configuration of a ModelarDB node.
121+
message UpdateConfiguration {
122+
enum Setting {
123+
MULTIVARIATE_RESERVED_MEMORY_IN_BYTES = 0;
124+
UNCOMPRESSED_RESERVED_MEMORY_IN_BYTES = 1;
125+
COMPRESSED_RESERVED_MEMORY_IN_BYTES = 2;
126+
TRANSFER_BATCH_SIZE_IN_BYTES = 3;
127+
TRANSFER_TIME_IN_SECONDS = 4;
128+
}
129+
130+
// Setting to update in the configuration.
131+
Setting setting = 1;
132+
133+
// New value for the setting.
134+
optional uint64 new_value = 2;
135+
}
136+
137+
// Metadata for a ModelarDB database instance.
138+
message DatabaseMetadata {
139+
// Names of the tables in the database.
140+
repeated string table_names = 1;
141+
}

Apache-Arrow-Flight-Tester/protobuf/protocol_pb2.py

Lines changed: 62 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)