Skip to content

Commit c9f848b

Browse files
committed
json2code: support chunked_fifo
Currently json2code only supports std::vector as a list type, but this results in unavoidable large allocations for even modest response sizes, e.g., is is not uncommon for sizeof(elem_type) for these vectors to be 100 - 1000 bytes for simple to moderately complex response types, so then a mere ~1200 to ~120 objects in the vector are enough to result in allocations > 128K, a problem for seastar applications which must avoid large allocations (because of fragmentation). To avoid this problem, support chunked_fifo as a second type for lists in json2code. Use the type "chunked_array" instead of "array" to use it and the generated code will use chunked_fifo instead of vector. Also adds tests for the new use case in the json2code test.
1 parent 489eb67 commit c9f848b

File tree

5 files changed

+52
-12
lines changed

5 files changed

+52
-12
lines changed

include/seastar/json/json_elements.hh

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,12 @@
2626
#include <vector>
2727
#endif
2828

29+
#include <seastar/core/chunked_fifo.hh>
2930
#include <seastar/core/do_with.hh>
31+
#include <seastar/core/iostream.hh>
3032
#include <seastar/core/loop.hh>
31-
#include <seastar/json/formatter.hh>
3233
#include <seastar/core/sstring.hh>
33-
#include <seastar/core/iostream.hh>
34+
#include <seastar/json/formatter.hh>
3435
#include <seastar/util/modules.hh>
3536

3637
namespace seastar {
@@ -149,13 +150,15 @@ private:
149150
};
150151

151152
/**
152-
* json_list is based on std vector implementation.
153+
* json_list_template is an array type based on a
154+
* container type passed as a template parameter, as we want to
155+
* have flavors based on both vector and chunked_fifo.
153156
*
154157
* When values are added with push it is set the "set" flag to true
155158
* hence will be included in the parsed object
156159
*/
157-
template<class T>
158-
class json_list : public json_base_element {
160+
template <class T, class Container>
161+
class json_list_template : public json_base_element {
159162
public:
160163

161164
/**
@@ -186,7 +189,7 @@ public:
186189
* iteration and that it's elements can be assigned to the list elements
187190
*/
188191
template<class C>
189-
json_list& operator=(const C& list) {
192+
json_list_template& operator=(const C& list) {
190193
_elements.clear();
191194
for (auto i : list) {
192195
push(i);
@@ -196,9 +199,16 @@ public:
196199
virtual future<> write(output_stream<char>& s) const override {
197200
return formatter::write(s, _elements);
198201
}
199-
std::vector<T> _elements;
202+
203+
Container _elements;
200204
};
201205

206+
template <typename T>
207+
using json_list = json_list_template<T, std::vector<T>>;
208+
209+
template <typename T>
210+
using json_chunked_list = json_list_template<T, seastar::chunked_fifo<T>>;
211+
202212
class jsonable {
203213
public:
204214
jsonable() = default;

scripts/seastar-json2code.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,14 @@ def valid_type(param):
9797
trace_err("Type [", param, "] not defined")
9898
return param
9999

100+
# Map from json list types to the C++ implementing type
101+
LIST_TO_IMPL = {"array": "json_list", "chunked_array": "json_chunked_list"}
100102

101-
def type_change(param, member):
102-
if param == "array":
103+
def is_array_type(type: str):
104+
return type in LIST_TO_IMPL
105+
106+
def type_change(param: str, member):
107+
if is_array_type(param):
103108
if "items" not in member:
104109
trace_err("array without item declaration in ", param)
105110
return ""
@@ -111,7 +116,8 @@ def type_change(param, member):
111116
else:
112117
trace_err("array items with no type or ref declaration ", param)
113118
return ""
114-
return "json_list< " + valid_type(t) + " >"
119+
return LIST_TO_IMPL[param] + "< " + valid_type(t) + " >"
120+
115121
return "json_element< " + valid_type(param) + " >"
116122

117123

@@ -371,7 +377,7 @@ def is_model_valid(name, model):
371377
properties = getitem(model[name], "properties", name)
372378
for var in properties:
373379
type = getitem(properties[var], "type", name + ":" + var)
374-
if type == "array":
380+
if is_array_type(type):
375381
items = getitem(properties[var], "items", name + ":" + var)
376382
try:
377383
type = getitem(items, "type", name + ":" + var + ":items")

tests/unit/api.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@
8282
"VAL2",
8383
"VAL3"
8484
]
85+
},
86+
"array_var": {
87+
"type": "array",
88+
"items": {
89+
"type": "int"
90+
}
91+
},
92+
"chunked_array_var": {
93+
"type": "chunked_array",
94+
"items": {
95+
"type": "int"
96+
}
8597
}
8698
}
8799
}

tests/unit/json2code_test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ def test_missing_path_param(self):
7979
self.assertEqual(response['message'], 'Not found')
8080
self.assertEqual(response['code'], 404)
8181

82+
def test_arrays(self):
83+
response = self._do_query('x', 'x', 'VAL2')
84+
expected = [1, 2, 3]
85+
86+
self.assertEqual(response['array_var'], expected)
87+
self.assertEqual(response['chunked_array_var'], expected)
88+
8289
def _do_query(self, var1: str, var2: str, query_enum: str):
8390
def query(streaming: bool = False):
8491
params = urllib.parse.urlencode({

tests/unit/rest_api_httpd.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ void set_routes(routes& r) {
5050
query_enum v = str2query_enum(req.query_parameters.at("query_enum"));
5151
// This demonstrate enum conversion
5252
obj.enum_var = v;
53-
stream_enum is_streaming =str2stream_enum(req.query_parameters.at("stream_enum"));
53+
54+
std::vector<int> vec123{1, 2, 3};
55+
obj.array_var = vec123;
56+
obj.chunked_array_var = vec123;
57+
58+
auto use_streaming = req.query_parameters.at("use_streaming");
5459

5560
if (use_streaming != "false" && use_streaming != "true") {
5661
throw bad_param_exception("param use_streaming must be true or false");

0 commit comments

Comments
 (0)