mz_persist_proc/
lib.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
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 in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Internal utility proc-macros for persist.
17//!
18//! Note: This is separate from the `mz_persist_client` crate because
19//! `proc-macro` crates are only allowed to export procedural macros and nothing
20//! else.
21
22use proc_macro::TokenStream;
23use proc_macro2::TokenStream as TokenStream2;
24use quote::quote;
25use syn::{ItemFn, ReturnType, parse_macro_input};
26
27/// Persist wrapper around the `test` macro.
28///
29/// The wrapper automatically runs the test with various interesting
30/// configurations.
31#[proc_macro_attribute]
32pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
33    test_impl(attr, item)
34}
35
36/// Implementation for the `#[mz_persist_proc::test]` macro.
37fn test_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
38    let args = TokenStream2::from(attr);
39    let item = parse_macro_input!(item as ItemFn);
40
41    let attrs = &item.attrs;
42    let async_ = &item.sig.asyncness;
43    let await_ = if async_.is_some() {
44        quote! {.await}
45    } else {
46        quote! {}
47    };
48    let inputs = &item.sig.inputs;
49    let body = &item.block;
50    let test_name = &item.sig.ident;
51
52    // Note that Rust does not allow us to have a test function with
53    // #[should_panic] that has a non-unit return value.
54    let ret = match &item.sig.output {
55        ReturnType::Default => quote! {},
56        ReturnType::Type(_, type_) => quote! {-> #type_},
57    };
58
59    quote! {
60        #[::mz_ore::test(
61            #args
62        )]
63        #(#attrs)*
64        #async_ fn #test_name() #ret {
65            #async_ fn test_impl(#inputs) #ret {
66              #body
67            }
68
69            let dyncfgs = [
70                {
71                    // Inline writes disabled
72                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
73                    x.add_dynamic("persist_inline_writes_single_max_bytes", ::mz_dyncfg::ConfigVal::Usize(0));
74                    x.add_dynamic("persist_inline_writes_total_max_bytes", ::mz_dyncfg::ConfigVal::Usize(0));
75                    x
76                },
77                {
78                    // Inline writes enabled
79                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
80                    x.add_dynamic("persist_inline_writes_single_max_bytes", ::mz_dyncfg::ConfigVal::Usize(4 * 1024));
81                    x.add_dynamic("persist_inline_writes_total_max_bytes", ::mz_dyncfg::ConfigVal::Usize(1024 * 1024));
82                    x
83                },
84                {
85                    // Stress inline writes backpressure
86                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
87                    x.add_dynamic("persist_inline_writes_single_max_bytes", ::mz_dyncfg::ConfigVal::Usize(4 * 1024));
88                    x.add_dynamic("persist_inline_writes_total_max_bytes", ::mz_dyncfg::ConfigVal::Usize(0));
89                    x
90                },
91                {
92                    // Enable new compaction tracking / claiming
93                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
94                    x.add_dynamic("persist_claim_unclaimed_compactions", ::mz_dyncfg::ConfigVal::Bool(true));
95                    x
96                },
97                {
98                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
99                    x.add_dynamic("persist_record_schema_id", ::mz_dyncfg::ConfigVal::Bool(true));
100                    x
101                },
102                {
103                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
104                    x.add_dynamic("persist_encoding_enable_dictionary", ::mz_dyncfg::ConfigVal::Bool(true));
105                    x
106                },
107                {
108                    let mut x = ::mz_dyncfg::ConfigUpdates::default();
109                    x.add_dynamic("persist_batch_max_run_len", ::mz_dyncfg::ConfigVal::Usize(4));
110                    x
111                },
112            ];
113
114            for (idx, dyncfgs) in dyncfgs.into_iter().enumerate() {
115                let debug = dyncfgs.updates.iter().map(|(name, val)| {
116                    format!(" {}={:?}", name, val.val.clone().unwrap())
117                }).collect::<String>();
118                eprintln!("mz_persist_proc::test {}{}", idx, debug);
119                test_impl(dyncfgs)#await_
120            }
121          }
122    }
123    .into()
124}