diff --git a/buildscripts/idl/idl/ast.py b/buildscripts/idl/idl/ast.py index 9bf8552d0c7..7763f2cd2ee 100644 --- a/buildscripts/idl/idl/ast.py +++ b/buildscripts/idl/idl/ast.py @@ -492,6 +492,8 @@ class ServerParameter(common.SourceLocation): self.is_deprecated = False # type: bool + self.annotations = None # type: Dict[str, Any] + super(ServerParameter, self).__init__(file_name, line, column) diff --git a/buildscripts/idl/idl/binder.py b/buildscripts/idl/idl/binder.py index 035b720c509..61145310013 100644 --- a/buildscripts/idl/idl/binder.py +++ b/buildscripts/idl/idl/binder.py @@ -1567,6 +1567,7 @@ def _bind_server_parameter(ctxt, param): ast_param.omit_in_ftdc = param.omit_in_ftdc ast_param.is_deprecated = param.is_deprecated + ast_param.annotations = param.annotations ast_param.set_at = _bind_server_parameter_set_at(ctxt, param) if ast_param.set_at is None: diff --git a/buildscripts/idl/idl/generator.py b/buildscripts/idl/idl/generator.py index 7afc29bde86..68ae007c17f 100644 --- a/buildscripts/idl/idl/generator.py +++ b/buildscripts/idl/idl/generator.py @@ -30,6 +30,7 @@ import hashlib import io import itertools +import math import os import re import sys @@ -3171,6 +3172,86 @@ class _CppSourceFileWriter(_CppFileWriterBase): f'{{"{entry.name}", {"true" if entry.generic_field_info.get_should_forward() else "false"} }},' ) + def _gen_annotations_elem(self, builder, key, value, depth=0): + # type: (str, Optional[str], Any, int) -> None + """Dispatch an annotation value to the appropriate generator.""" + if value is None: + self._gen_annotations_null(builder, key) + elif isinstance(value, dict): + self._gen_annotations_obj(builder, key, value, depth) + elif isinstance(value, list): + self._gen_annotations_arr(builder, key, value, depth) + else: + self._gen_annotations_scalar(builder, key, value) + + def _gen_annotations_obj(self, builder, key, obj, depth): + # type: (str, Optional[str], Dict[str, Any], int) -> None + """Emit a BSONObjBuilder sub-object for a dict.""" + sub = "sub%d" % depth + start = "%s.subobjStart(%s)" % (builder, key) if key else "%s.subobjStart()" % builder + with self._block("{", "}"): + self._writer.write_line("BSONObjBuilder %s(%s);" % (sub, start)) + for k, v in obj.items(): + self._gen_annotations_elem(sub, _encaps(k), v, depth + 1) + + def _gen_annotations_arr(self, builder, key, items, depth): + # type: (str, Optional[str], List[Any], int) -> None + """Emit a BSONArrayBuilder sub-array for a list.""" + arr = "arr%d" % depth + start = "%s.subarrayStart(%s)" % (builder, key) if key else "%s.subarrayStart()" % builder + with self._block("{", "}"): + self._writer.write_line("BSONArrayBuilder %s(%s);" % (arr, start)) + for item in items: + self._gen_annotations_elem(arr, None, item, depth + 1) + + def _gen_annotations_null(self, builder, key): + # type: (str, Optional[str]) -> None + """Emit an appendNull call.""" + if key: + self._writer.write_line("%s.appendNull(%s);" % (builder, key)) + else: + self._writer.write_line("%s.appendNull();" % builder) + + def _gen_annotations_scalar(self, builder, key, value): + # type: (str, Optional[str], Any) -> None + """Emit an append call for a scalar value.""" + if isinstance(value, bool): + literal = "true" if value else "false" + elif isinstance(value, int): + literal = str(value) + elif isinstance(value, float): + if not math.isfinite(value): + raise ValueError( + f"Unsupported annotation value {value!r} for key {key!r}: " + f"NaN and infinity are not valid C++ literals." + ) + literal = str(value) + elif isinstance(value, str): + literal = _encaps(value) + else: + raise ValueError( + f"Unsupported annotation type {type(value).__name__} for {key}: " + f"expected bool, int, float, or str." + ) + + if key: + self._writer.write_line("%s.append(%s, %s);" % (builder, key, literal)) + else: + self._writer.write_line("%s.append(%s);" % (builder, literal)) + + def _gen_server_parameter_annotations(self, var_name, param): + # type: (str, ast.ServerParameter) -> None + """Generate setAnnotations call if the parameter has annotations.""" + if param.annotations: + with self.get_initializer_lambda( + "static const auto kAnnotations", return_type="BSONObj" + ): + self._writer.write_line("BSONObjBuilder builder;") + for k, v in param.annotations.items(): + self._gen_annotations_elem("builder", _encaps(k), v) + self._writer.write_line("return builder.obj();") + self._writer.write_line("%s->setAnnotations(kAnnotations);" % var_name) + def _gen_server_parameter_specialized(self, param): # type: (ast.ServerParameter) -> None """Generate a specialized ServerParameter.""" @@ -3187,6 +3268,8 @@ class _CppSourceFileWriter(_CppFileWriterBase): if param.is_deprecated: self._writer.write_line("sp->setIsDeprecated(true);") + self._gen_server_parameter_annotations("sp", param) + self._writer.write_line("return std::move(sp);") def _gen_server_parameter_class_definitions(self, param): @@ -3261,6 +3344,8 @@ class _CppSourceFileWriter(_CppFileWriterBase): if param.is_deprecated: self._writer.write_line("ret->setIsDeprecated(true);") + self._gen_server_parameter_annotations("ret", param) + if param.default and not (param.cpp_vartype and param.cpp_varname): # Only need to call setDefault() if we haven't in-place initialized the declared var. self._writer.write_line( diff --git a/buildscripts/idl/idl/parser.py b/buildscripts/idl/idl/parser.py index 3a542b94112..96fa5b7c9cb 100644 --- a/buildscripts/idl/idl/parser.py +++ b/buildscripts/idl/idl/parser.py @@ -917,6 +917,13 @@ def _parse_server_parameter_class(ctxt, node): return spc +def _parse_annotations(ctxt, node): + # type: (errors.ParserContext, yaml.nodes.MappingNode) -> Dict[str, Any] + """Parse an annotations block as an opaque YAML mapping.""" + constructor = yaml.constructor.SafeConstructor() + return constructor.construct_object(node, deep=True) + + def _parse_server_parameter(ctxt, spec, name, node): # type: (errors.ParserContext, syntax.IDLSpec, str, Union[yaml.nodes.MappingNode, yaml.nodes.ScalarNode, yaml.nodes.SequenceNode]) -> None """Parse a server_parameters section in the IDL file.""" @@ -950,6 +957,7 @@ def _parse_server_parameter(ctxt, spec, name, node): "omit_in_ftdc": _RuleDesc("bool_scalar"), "cpp_class": _RuleDesc("scalar_or_mapping", mapping_parser_func=map_class), "is_deprecated": _RuleDesc("bool_scalar"), + "annotations": _RuleDesc("mapping", mapping_parser_func=_parse_annotations), }, ) diff --git a/buildscripts/idl/idl/syntax.py b/buildscripts/idl/idl/syntax.py index f04c409c25a..2ba70aa848d 100644 --- a/buildscripts/idl/idl/syntax.py +++ b/buildscripts/idl/idl/syntax.py @@ -858,6 +858,8 @@ class ServerParameter(common.SourceLocation): self.is_deprecated = False # type: bool + self.annotations = None # type: Dict[str, Any] + super(ServerParameter, self).__init__(file_name, line, column) diff --git a/buildscripts/idl/idl_schema.yml b/buildscripts/idl/idl_schema.yml index 3f0b9ee21ad..96695f08dfe 100644 --- a/buildscripts/idl/idl_schema.yml +++ b/buildscripts/idl/idl_schema.yml @@ -690,6 +690,11 @@ properties: description: > Marks the server parameter as deprecated. Warns users if the server parameter is ever used. Defaults to false. + annotations: + type: object + description: > + Opaque metadata block attached to the server parameter. Parsed as-is and + exposed at runtime via ServerParameter::annotations() as a BSONObj. on_update: type: string description: > diff --git a/buildscripts/idl/tests/context.py b/buildscripts/idl/tests/context.py index 52191c5bd48..facce22d83f 100644 --- a/buildscripts/idl/tests/context.py +++ b/buildscripts/idl/tests/context.py @@ -39,3 +39,4 @@ import idl.errors # noqa: F401 import idl.generator # noqa: F401 import idl.parser # noqa: F401 import idl.syntax # noqa: F401 +import idl.writer # noqa: F401 diff --git a/buildscripts/idl/tests/test_binder.py b/buildscripts/idl/tests/test_binder.py index 177b82bfa8b..b5d47350c26 100644 --- a/buildscripts/idl/tests/test_binder.py +++ b/buildscripts/idl/tests/test_binder.py @@ -2364,6 +2364,76 @@ class TestBinder(testcase.IDLTestcase): """) ) + def test_server_parameter_annotations_positive(self): + # type: () -> None + """Positive server parameter annotations test cases.""" + + # server parameter with cpp_varname and simple annotations. + self.assert_bind( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: + query_knob: + wire_name: fooWire + applicability: queryShape + """) + ) + + # server parameter with cpp_class and annotations. + self.assert_bind( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_class: baz + annotations: + query_knob: + wire_name: fooWire + """) + ) + + # annotations with nested mapping (fcv sub-block). + self.assert_bind( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: + query_knob: + wire_name: fooWire + applicability: queryShape + fcv: + min: "9.0" + """) + ) + + # multiple annotation keys. + self.assert_bind( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: + query_knob: + wire_name: fooWire + security: + audit: true + """) + ) + def test_server_parameter_negative(self): # type: () -> None """Negative server parameter test cases.""" diff --git a/buildscripts/idl/tests/test_generator.py b/buildscripts/idl/tests/test_generator.py index 0c9aabb9ed6..e74233db32f 100644 --- a/buildscripts/idl/tests/test_generator.py +++ b/buildscripts/idl/tests/test_generator.py @@ -36,6 +36,7 @@ idl base directory: $ coverage run run_tests.py && coverage html """ +import datetime import io import os import unittest @@ -475,6 +476,157 @@ class TestGenerator(testcase.IDLTestcase): ) self.assertIn(expected, header) + def test_server_parameter_annotations_generates_set_call(self) -> None: + """Test generation of setAnnotations call for annotated server parameter.""" + _, source = self.assert_generate( + self.view_test_common_types + + dedent(""" + server_parameters: + testAnnotatedParam: + description: "Test annotated parameter" + set_at: ["startup", "runtime"] + redact: false + cpp_varname: testParameter + annotations: + query_knob: + wire_name: testWire + applicability: queryShape + """) + ) + + # Verify the BSON builder lambda is generated. + self.assertIn("kAnnotations", source) + self.assertIn("BSONObjBuilder builder", source) + self.assertIn('"query_knob"', source) + self.assertIn('"wire_name"', source) + self.assertIn('"testWire"', source) + self.assertIn('"applicability"', source) + self.assertIn('"queryShape"', source) + + # Verify setAnnotations call. + self.assertIn("setAnnotations(kAnnotations)", source) + + def test_server_parameter_no_annotations_no_set_call(self) -> None: + """Test that server parameter without annotations does not generate setAnnotations.""" + _, source = self.assert_generate( + self.view_test_common_types + + dedent(""" + server_parameters: + testUnannotatedParam: + description: "Test unannotated parameter" + set_at: ["startup", "runtime"] + redact: false + cpp_varname: testParameter + """) + ) + + self.assertNotIn("setAnnotations", source) + self.assertNotIn("kAnnotations", source) + + def test_server_parameter_nested_annotations_bson(self) -> None: + """Test nested annotation values produce correct nested BSON output.""" + _, source = self.assert_generate( + self.view_test_common_types + + dedent(""" + server_parameters: + testNestedParam: + description: "Test nested annotations" + set_at: ["startup", "runtime"] + redact: false + cpp_varname: testParameter + annotations: + query_knob: + wire_name: testNestedWire + applicability: queryShape + fcv: + min: "9.0" + """) + ) + + # Verify nested BSON structure. + self.assertIn("kAnnotations", source) + self.assertIn("subobjStart", source) + self.assertIn('"fcv"', source) + self.assertIn('"min"', source) + self.assertIn('"9.0"', source) + + # Verify setAnnotations call. + self.assertIn("setAnnotations(kAnnotations)", source) + + def test_server_parameter_annotations_mixed_types_bson(self) -> None: + """Test annotation values of different YAML types produce correct BSON output.""" + _, source = self.assert_generate( + self.view_test_common_types + + dedent(""" + server_parameters: + testMixedParam: + description: "Test mixed-type annotations" + set_at: ["startup", "runtime"] + redact: false + cpp_varname: testParameter + annotations: + query_knob: + wire_name: testMixedWire + pqs_settable: true + max_retries: 3 + applicability: + - queryShape + - opCtx + """) + ) + + # Verify string value. + self.assertIn('"testMixedWire"', source) + + # Verify boolean value appended. + self.assertIn('append("pqs_settable", true)', source) + + # Verify integer value appended. + self.assertIn('append("max_retries", 3)', source) + + # Verify array value. + self.assertIn("subarrayStart", source) + self.assertIn('"queryShape"', source) + self.assertIn('"opCtx"', source) + + # Verify setAnnotations call. + self.assertIn("setAnnotations(kAnnotations)", source) + + def test_server_parameter_annotations_rejects_unquoted_date(self) -> None: + """YAML auto-coerces unquoted dates to datetime.date; the generator must reject them.""" + with self.assertRaises(ValueError): + self.assert_generate( + self.view_test_common_types + + dedent(""" + server_parameters: + testDateParam: + description: "Test param with unquoted date annotation" + set_at: ["startup", "runtime"] + redact: false + cpp_varname: testParameter + annotations: + wire_name: 2026-04-14 + """) + ) + + def test_server_parameter_annotations_accepts_quoted_date(self) -> None: + """A quoted date stays a string and flows through as a C++ string literal.""" + _, source = self.assert_generate( + self.view_test_common_types + + dedent(""" + server_parameters: + testQuotedDateParam: + description: "Test param with quoted date annotation" + set_at: ["startup", "runtime"] + redact: false + cpp_varname: testParameter + annotations: + wire_name: "2026-04-14" + """) + ) + self.assertIn('"2026-04-14"', source) + self.assertIn("setAnnotations(kAnnotations)", source) + def test_command_view_type_generates_anchor(self) -> None: """Test anchor generation on command with view parameter.""" header, _ = self.assert_generate( @@ -1700,5 +1852,136 @@ class TestGenerator(testcase.IDLTestcase): ) +class TestAnnotationsCodegen(testcase.IDLTestcase): + """Direct unit tests for the _gen_annotations_* helpers on _CppSourceFileWriter.""" + + def _emit(self, key, value, depth=0): + # type: (object, object, int) -> str + """Run _gen_annotations_elem on a fresh writer and return the generated text.""" + stream = io.StringIO() + source_writer = idl.generator._CppSourceFileWriter( + idl.writer.IndentedTextWriter(stream), target_arch="" + ) + source_writer._gen_annotations_elem("builder", key, value, depth=depth) + return stream.getvalue() + + def test_scalar_string_keyed(self): + self.assertEqual(self._emit('"k"', "hello"), 'builder.append("k", "hello");\n') + + def test_scalar_string_unkeyed(self): + self.assertEqual(self._emit(None, "hello"), 'builder.append("hello");\n') + + def test_scalar_bool_true(self): + self.assertEqual(self._emit('"k"', True), 'builder.append("k", true);\n') + + def test_scalar_bool_false(self): + self.assertEqual(self._emit('"k"', False), 'builder.append("k", false);\n') + + def test_scalar_int(self): + self.assertEqual(self._emit('"k"', 3), 'builder.append("k", 3);\n') + + def test_scalar_float(self): + self.assertEqual(self._emit('"k"', 3.25), 'builder.append("k", 3.25);\n') + + def test_null_keyed(self): + self.assertEqual(self._emit('"k"', None), 'builder.appendNull("k");\n') + + def test_null_unkeyed(self): + self.assertEqual(self._emit(None, None), "builder.appendNull();\n") + + def test_empty_dict(self): + expected = dedent("""\ + { + BSONObjBuilder sub0(builder.subobjStart("k")); + } + """) + self.assertEqual(self._emit('"k"', {}), expected) + + def test_flat_dict(self): + expected = dedent("""\ + { + BSONObjBuilder sub0(builder.subobjStart("k")); + sub0.append("a", 1); + } + """) + self.assertEqual(self._emit('"k"', {"a": 1}), expected) + + def test_nested_dict_depth_counter(self): + # Depth increments so nested sub-builder names don't collide. + expected = dedent("""\ + { + BSONObjBuilder sub0(builder.subobjStart("root")); + { + BSONObjBuilder sub1(sub0.subobjStart("outer")); + sub1.append("inner", 1); + } + } + """) + self.assertEqual(self._emit('"root"', {"outer": {"inner": 1}}), expected) + + def test_empty_list(self): + expected = dedent("""\ + { + BSONArrayBuilder arr0(builder.subarrayStart("k")); + } + """) + self.assertEqual(self._emit('"k"', []), expected) + + def test_flat_list(self): + expected = dedent("""\ + { + BSONArrayBuilder arr0(builder.subarrayStart("k")); + arr0.append(1); + arr0.append(2); + } + """) + self.assertEqual(self._emit('"k"', [1, 2]), expected) + + def test_list_of_dicts(self): + # Items inside a list use the unkeyed subobjStart form and advance depth. + expected = dedent("""\ + { + BSONArrayBuilder arr0(builder.subarrayStart("k")); + { + BSONObjBuilder sub1(arr0.subobjStart()); + sub1.append("a", 1); + } + { + BSONObjBuilder sub1(arr0.subobjStart()); + sub1.append("b", 2); + } + } + """) + self.assertEqual(self._emit('"k"', [{"a": 1}, {"b": 2}]), expected) + + def test_scalar_rejects_date(self): + with self.assertRaises(ValueError): + self._emit('"k"', datetime.date(2026, 4, 14)) + + def test_scalar_rejects_datetime(self): + with self.assertRaises(ValueError): + self._emit('"k"', datetime.datetime(2026, 4, 14, 10, 30)) + + def test_scalar_rejects_nan(self): + with self.assertRaises(ValueError): + self._emit('"k"', float("nan")) + + def test_scalar_rejects_positive_inf(self): + with self.assertRaises(ValueError): + self._emit('"k"', float("inf")) + + def test_scalar_rejects_negative_inf(self): + with self.assertRaises(ValueError): + self._emit('"k"', float("-inf")) + + def test_scalar_rejects_bytes(self): + with self.assertRaises(ValueError): + self._emit('"k"', b"hello") + + def test_scalar_rejects_set(self): + with self.assertRaises(ValueError): + self._emit('"k"', {"a", "b"}) + + if __name__ == "__main__": unittest.main() diff --git a/buildscripts/idl/tests/test_parser.py b/buildscripts/idl/tests/test_parser.py index dbe04ad64bb..eea634c900a 100644 --- a/buildscripts/idl/tests/test_parser.py +++ b/buildscripts/idl/tests/test_parser.py @@ -1566,6 +1566,74 @@ class TestParser(testcase.IDLTestcase): idl.errors.ERROR_ID_IS_NODE_TYPE_SCALAR_OR_MAPPING, ) + def test_server_parameter_annotations_positive(self): + # type: () -> None + """Positive server parameter annotations tests.""" + + # server parameter with annotations mapping. + self.assert_parse( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: + query_knob: + wire_name: fooWire + applicability: queryShape + """) + ) + + def test_server_parameter_annotations_negative(self): + # type: () -> None + """Negative server parameter annotations tests.""" + + # annotations as a scalar should fail. Must be a mapping. + self.assert_parse_fail( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: some_scalar_value + """), + idl.errors.ERROR_ID_IS_NODE_TYPE, + ) + + # annotations as a sequence should fail. Must be a mapping. + self.assert_parse_fail( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: + - item1 + - item2 + """), + idl.errors.ERROR_ID_IS_NODE_TYPE, + ) + + # annotations as null should fail. Must be a mapping. + self.assert_parse_fail( + textwrap.dedent(""" + server_parameters: + foo: + set_at: startup + description: bar + redact: false + cpp_varname: baz + annotations: null + """), + idl.errors.ERROR_ID_IS_NODE_TYPE, + ) + def test_feature_flag(self): # type: () -> None """Test feature flag.""" diff --git a/src/mongo/db/server_parameter.h b/src/mongo/db/server_parameter.h index 26a7ca39922..d4c8dde6391 100644 --- a/src/mongo/db/server_parameter.h +++ b/src/mongo/db/server_parameter.h @@ -387,6 +387,14 @@ public: return _isDeprecated; } + void setAnnotations(BSONObj annotations) { + _annotations = std::move(annotations); + } + + BSONObj annotations() const { + return _annotations; + } + protected: // Helper for translating setParameter values from BSON to string. StatusWith _coerceToString(const BSONElement&); @@ -410,6 +418,7 @@ private: bool _testOnly = false; bool _redact = false; bool _isOmittedInFTDC = false; + BSONObj _annotations; ParameterGatingFeatureFlag* _featureFlag = nullptr; boost::optional _minFCV; diff --git a/src/mongo/idl/server_parameter_with_storage_test.cpp b/src/mongo/idl/server_parameter_with_storage_test.cpp index 56be2d7973e..6447cac604b 100644 --- a/src/mongo/idl/server_parameter_with_storage_test.cpp +++ b/src/mongo/idl/server_parameter_with_storage_test.cpp @@ -298,6 +298,43 @@ TEST(IDLServerParameterWithStorage, exportedDefaults) { ASSERT_EQ(test::kUgly_complicated_name_spDefault, true); } +TEST(IDLServerParameterWithStorage, annotationsAccessible) { + auto* sp = getNodeServerParameter("storageIntAnnotated"); + ASSERT_BSONOBJ_EQ( + sp->annotations(), + BSON("query_knob" << BSON("wire_name" << "intAnnotatedWire" + << "applicability" << BSON_ARRAY("queryShape") + << "fcv" << BSON("min" << "9.0")))); +} + +TEST(IDLServerParameterWithStorage, noAnnotationsReturnsEmpty) { + auto* sp = getNodeServerParameter("stdIntDeclared"); + ASSERT_TRUE(sp->annotations().isEmpty()); +} + +TEST(IDLServerParameterWithStorage, annotationsArrayOfObjects) { + auto* sp = getNodeServerParameter("storageIntArrayOfObjects"); + ASSERT_BSONOBJ_EQ(sp->annotations(), + BSON("items" << BSON_ARRAY(BSON("name" << "first" << "value" << 1) + << BSON("name" << "second" << "value" << 2)))); +} + +TEST(IDLServerParameterWithStorage, annotationsNestedArrays) { + auto* sp = getNodeServerParameter("storageIntNestedArrays"); + ASSERT_BSONOBJ_EQ(sp->annotations(), + BSON("matrix" << BSON_ARRAY(BSON_ARRAY(1 << 2) << BSON_ARRAY(3 << 4)))); +} + +TEST(IDLServerParameterWithStorage, annotationsEmptyArray) { + auto* sp = getNodeServerParameter("storageIntEmptyArray"); + ASSERT_BSONOBJ_EQ(sp->annotations(), BSON("tags" << BSONArray())); +} + +TEST(IDLServerParameterWithStorage, annotationsOnClusterParameter) { + auto* sp = getClusterServerParameter("testClusterServerParameter"); + ASSERT_BSONOBJ_EQ(sp->annotations(), BSON("cluster_meta" << BSON("scope" << "global"))); +} + // Test that the RAIIServerParameterControllerForTest works correctly on IDL-generated types. TEST(IDLServerParameterWithStorage, RAIIServerParameterController) { // Test int diff --git a/src/mongo/idl/server_parameter_with_storage_test.idl b/src/mongo/idl/server_parameter_with_storage_test.idl index a86e2251faa..4892a84361f 100644 --- a/src/mongo/idl/server_parameter_with_storage_test.idl +++ b/src/mongo/idl/server_parameter_with_storage_test.idl @@ -108,6 +108,9 @@ server_parameters: on_update: "onUpdateTestClusterServerParameter" redact: false omit_in_ftdc: false + annotations: + cluster_meta: + scope: global storageIntDeprecated: set_at: [startup, runtime] @@ -120,3 +123,55 @@ server_parameters: lt: 1000 redact: false is_deprecated: true + + storageIntAnnotated: + set_at: [startup, runtime] + description: "Server parameter with annotations." + cpp_vartype: AtomicWord + cpp_varname: gIntAnnotated + default: 7 + redact: false + annotations: + query_knob: + wire_name: intAnnotatedWire + applicability: [queryShape] + fcv: + min: "9.0" + + storageIntArrayOfObjects: + set_at: [startup, runtime] + description: "Parameter with array-of-objects annotations." + cpp_vartype: AtomicWord + cpp_varname: gIntArrayOfObjects + default: 1 + redact: false + annotations: + items: + - name: first + value: 1 + - name: second + value: 2 + + storageIntNestedArrays: + set_at: [startup, runtime] + description: "Parameter with nested array annotations." + cpp_vartype: AtomicWord + cpp_varname: gIntNestedArrays + default: 1 + redact: false + annotations: + matrix: + - - 1 + - 2 + - - 3 + - 4 + + storageIntEmptyArray: + set_at: [startup, runtime] + description: "Parameter with empty array annotation." + cpp_vartype: AtomicWord + cpp_varname: gIntEmptyArray + default: 1 + redact: false + annotations: + tags: []