@@ -15,6 +15,7 @@ use std::sync::LazyLock;
1515use std:: time:: Duration ;
1616
1717use enumset:: EnumSet ;
18+ use paste:: paste;
1819use serde:: { Deserialize , Serialize } ;
1920use serde_with:: serde_as;
2021
@@ -463,20 +464,6 @@ pub struct CommonOptions {
463464 #[ serde( flatten) ]
464465 pub gossip : GossipOptions ,
465466
466- /// Current in heavy development, do not enable this feature unless you are a contributor
467- #[ cfg_attr( feature = "schemars" , schemars( skip) ) ]
468- #[ serde( skip_serializing_if = "std::ops::Not::not" , default ) ]
469- pub experimental_enable_vqueues : bool ,
470-
471- /// When enabled, invocations that exhaust their memory budget will yield back to
472- /// the scheduler instead of consuming retry attempts. Requires all nodes in the
473- /// cluster to be running v1.7.0 or later because it introduces a new WAL variant.
474- ///
475- /// Since v1.6.3
476- #[ cfg_attr( feature = "schemars" , schemars( skip) ) ]
477- #[ serde( skip_serializing_if = "std::ops::Not::not" , default ) ]
478- pub experimental_enable_invoker_yield : bool ,
479-
480467 /// # HLC maximum drift
481468 ///
482469 /// Restate uses an internal hybrid-logical-clock (HLC) to track causality between
@@ -494,6 +481,103 @@ pub struct CommonOptions {
494481 #[ serde( default ) ]
495482 hlc_max_drift : FriendlyDuration ,
496483
484+ #[ serde( flatten) ]
485+ pub experimental : Experimental ,
486+ }
487+
488+ /// Declares the [`Experimental`] feature-flag struct from a list of feature names.
489+ ///
490+ /// Each entry is a bare identifier (optionally preceded by doc comments / attributes) inside
491+ /// `experimental! { ... }`. For a feature `foo` the macro generates:
492+ /// - a `experimental_enable_foo: bool` field on [`Experimental`] — this is the on-disk /
493+ /// JSON-schema name, so the configuration schema always exposes flags as
494+ /// `experimental_enable_<feature>`;
495+ /// - `Experimental::is_foo_enabled()` and `Experimental::set_foo(enable)` accessors;
496+ /// - an entry in [`Experimental::features`] keyed on the bare name `"foo"` (without the
497+ /// `experimental_enable_` prefix), which is what is surfaced through the admin `/version` API.
498+ ///
499+ /// Adding a new experimental flag is therefore a one-line change at the invocation site below:
500+ /// no other code needs to be touched for the flag to show up in `/version`.
501+ macro_rules! experimental {
502+ ( @gen_struct [ ] -> [ $( $body: tt) * ] ) => {
503+ #[ derive( Debug , Clone , Default , Serialize , Deserialize ) ]
504+ #[ cfg_attr( feature = "schemars" , derive( schemars:: JsonSchema ) ) ]
505+ #[ cfg_attr( feature = "schemars" , schemars( default ) ) ]
506+ #[ serde( rename_all = "kebab-case" ) ]
507+ pub struct Experimental {
508+ $( $body) *
509+ }
510+ } ;
511+ ( @gen_struct [ $( #[ $( $attrss: meta) * ] ) * $feat: ident $( , $( $tail: tt) * ) ?] -> [ $( $body: tt) * ] ) => {
512+ paste!{
513+ experimental!( @gen_struct [ $( $( $tail) * ) ?] -> [
514+ $( $body) *
515+
516+ $( #[ $( $attrss) * ] ) *
517+ #[ cfg_attr( feature = "schemars" , schemars( skip) ) ]
518+ #[ serde( skip_serializing_if = "std::ops::Not::not" , default ) ]
519+ [ <experimental_enable_ $feat>] : bool ,
520+ ] ) ;
521+ }
522+ } ;
523+ ( @gen_features [ ] -> [ $( $field: ident) * ] ) => {
524+ impl Experimental {
525+ pub fn features( & self ) -> std:: collections:: HashMap <std:: borrow:: Cow <' static , str >, bool > {
526+ let mut map = std:: collections:: HashMap :: default ( ) ;
527+ $(
528+ paste!{
529+ map. insert( std:: borrow:: Cow :: Borrowed ( stringify!( $field) ) , self . [ <experimental_enable_ $field>] ) ;
530+ }
531+ ) *
532+ map
533+ }
534+ }
535+ } ;
536+ ( @gen_features [ $( #[ $( $attrss: meta) * ] ) * $feat: ident $( , $( $tail: tt) * ) ?] -> [ $( $acc: ident) * ] ) => {
537+ experimental!( @gen_features [ $( $( $tail) * ) ?] -> [ $( $acc) * $feat] ) ;
538+ } ;
539+ ( @gen_getters [ ] -> [ $( $field: ident) * ] ) => {
540+ impl Experimental {
541+ $(
542+ paste!{
543+ pub fn [ <is_ $field _enabled>] ( & self ) -> bool {
544+ self . [ <experimental_enable_ $field>]
545+ }
546+
547+ pub fn [ <set_ $field>] ( & mut self , enable: bool ) {
548+ self . [ <experimental_enable_ $field>] = enable;
549+ }
550+ }
551+ ) *
552+ }
553+ } ;
554+ ( @gen_getters [ $( #[ $( $attrss: meta) * ] ) * $feat: ident $( , $( $tail: tt) * ) ?] -> [ $( $acc: ident) * ] ) => {
555+ experimental!( @gen_getters [ $( $( $tail) * ) ?] -> [ $( $acc) * $feat] ) ;
556+ } ;
557+
558+
559+ { $( $tokens: tt) * } => {
560+ experimental!( @gen_struct [ $( $tokens) * ] -> [ ] ) ;
561+ experimental!( @gen_features [ $( $tokens) * ] -> [ ] ) ;
562+ experimental!( @gen_getters [ $( $tokens) * ] -> [ ] ) ;
563+ } ;
564+ }
565+
566+ // List of experimental features. Add a new identifier below to introduce a flag; the
567+ // `experimental!` macro will generate the `experimental_enable_<name>` config field, the
568+ // `is_<name>_enabled()` / `set_<name>()` accessors, and the entry exposed (under the bare
569+ // name, without the `experimental_enable_` prefix) by the admin `/version` API.
570+ experimental ! {
571+ /// Current in heavy development, do not enable this feature unless you are a contributor
572+ vqueues,
573+
574+ /// When enabled, invocations that exhaust their memory budget will yield back to
575+ /// the scheduler instead of consuming retry attempts. Requires all nodes in the
576+ /// cluster to be running v1.7.0 or later because it introduces a new WAL variant.
577+ ///
578+ /// Since v1.6.3
579+ invoker_yield,
580+
497581 /// # Enables service protocol v7
498582 ///
499583 /// Introduced in Restate v1.7
@@ -502,9 +586,7 @@ pub struct CommonOptions {
502586 ///
503587 /// Once enabled, you **cannot** rollback back to previous versions
504588 /// where v7 is not supported < v1.7
505- #[ cfg_attr( feature = "schemars" , schemars( skip) ) ]
506- #[ serde( skip_serializing_if = "std::ops::Not::not" , default ) ]
507- pub experimental_allow_protocol_v7 : bool ,
589+ protocol_v7,
508590}
509591
510592serde_with:: with_prefix!( pub prefix_tokio_console "tokio_console_" ) ;
@@ -740,10 +822,8 @@ impl Default for CommonOptions {
740822 initialization_timeout : NonZeroFriendlyDuration :: from_secs_unchecked ( 5 * 60 ) ,
741823 disable_telemetry : false ,
742824 gossip : GossipOptions :: default ( ) ,
743- experimental_enable_vqueues : false ,
744- experimental_enable_invoker_yield : false ,
745825 hlc_max_drift : FriendlyDuration :: from_millis ( 5000 ) ,
746- experimental_allow_protocol_v7 : false ,
826+ experimental : Experimental :: default ( ) ,
747827 }
748828 }
749829}
0 commit comments