openanolis / dragonball-sandbox

Dragonball-sandbox is a collection of Rust crates to help build custom Virtual Machine Monitors and hypervisors.
Apache License 2.0
88 stars 41 forks source link

Add Versionize support #279

Closed wllenyj closed 1 year ago

wllenyj commented 1 year ago

Enhanced cross-version support.

Reason for This PR

In production use, we usually have multiple release branches, each branch corresponding to a minor version. When we develop between versions, there may be changes to fields of struct. This brings a lot of complexity and maintenance cost to version control.

Description of Changes

  1. Introduce semver as a version of the structure. Instead of a u16.
  2. Add multi-version support.
  3. serialize now only supports the current version, i.e. the current version of the application can only take snapshots of the current version.

Now writing a Versionize derive for a structure can look like this.

#[derive(Versionize)]                                        
struct TypeA {                                               
    a: u32,                                                  
    #[version(start = "2.7.7, 2.8.3", end = "2.7.11, 2.8.7")]
    b: u32,                                                  
    #[version(start = "2.7.9, 2.8.3", end = "2.7.11, 2.8.8")]
    c: u32,                                                  
    #[version(start = "2.8.11")]                             
    aaa: String,                                             
}                                                            

or enum:

#[derive(Versionize)]                                                     
enum EnumB {                                                              
    AAA(u32),                                                             
    #[version(start = "2.8.11", end = "2.8.22", default_fn = "def_BBB")]
    BBB(String),                                                        
}

Advantages:

The code generated by the above example:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use dbs_versionize::{Version, Versionize};
struct TypeA {
    a: u32,
    #[version(start = "2.7.7, 2.8.3", end = "2.7.11, 2.8.7")]
    b: u32,
    #[version(start = "2.7.9, 2.8.3", end = "2.7.11, 2.8.8")]
    c: u32,
    #[version(start = "2.8.11")]
    aaa: String,
}
impl Versionize for TypeA {
    fn serialize<W: std::io::Write>(
        &self,
        mut writer: W,
        version_map: &mut VersionMap,
    ) -> VersionizeResult<()> {
        let current = version_map.set_crate_version("version_test", "0.1.0")?;
        let mut copy_of_self = self.clone();
        Versionize::serialize(&copy_of_self.a, &mut writer, version_map)?;
        Versionize::serialize(&copy_of_self.aaa, &mut writer, version_map)?;
        Ok(())
    }
    fn deserialize<R: std::io::Read>(
        mut reader: R,
        version_map: &VersionMap,
    ) -> VersionizeResult<Self> {
        let source = version_map.get_crate_version("version_test")?;
        match (source.minor, source.patch) {
            (0..=6u64, _) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (7u64, 0u64..=6u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (7u64, 7u64..=8u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    c: Default::default(),
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (7u64, 9u64..=10u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    c: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (7u64, 11u64..) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (8u64, 0u64..=2u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (8u64, 3u64..=6u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    c: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (8u64, 7u64..=7u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (8u64, 8u64..=10u64) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: Default::default(),
                };
                Ok(object)
            }
            (8u64, 11u64..) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: <String as Versionize>::deserialize(
                        &mut reader,
                        version_map,
                    )?,
                };
                Ok(object)
            }
            (9u64.., _) => {
                let mut object = TypeA {
                    a: <u32 as Versionize>::deserialize(&mut reader, version_map)?,
                    b: Default::default(),
                    c: Default::default(),
                    aaa: <String as Versionize>::deserialize(
                        &mut reader,
                        version_map,
                    )?,
                };
                Ok(object)
            }
        }
    }
}
enum EnumB {
    AAA(u32),
    #[version(start = "2.8.11", end = "2.8.22", default_fn = "def_BBB")]
    BBB(String),
}
impl Versionize for EnumB {
    fn serialize<W: std::io::Write>(
        &self,
        mut writer: W,
        version_map: &mut VersionMap,
    ) ->VersionizeResult<()> {
        let current = version_map.set_crate_version("version_test", "0.1.0")?;
        let mut copy_of_self = self.clone();
        match self {
            Self::AAA(data_0) => {
                let index: u32 = 0u32;
                Versionize::serialize(&index, &mut writer, version_map)?;
                Versionize::serialize(data_0, &mut writer, version_map)?;
            }
            Self::BBB(..) => {
                let new_variant = self.def_BBB(current)?;
                new_variant.serialize(&mut writer, version_map)?;
            }
        }
        Ok(())
    }
    fn deserialize<R: std::io::Read>(
        mut reader: R,
        version_map: &VersionMap,
    ) -> VersionizeResult<Self> {
        let source = version_map.get_crate_version("version_test")?;
        let variant_index =
            <u32 as Versionize>::deserialize(&mut reader, version_map)?;
        match variant_index {
            0u32 => {
                let data_0 =
                    <u32 as Versionize>::deserialize(&mut reader, version_map)?;
                return Ok(Self::AAA(data_0));
            }
            1u32 => {
                let data_0 =
                    <String as Versionize>::deserialize(&mut reader, version_map)?;
                return Ok(Self::BBB(data_0));
            }
            x => {
                return Err(VersionizeError::Deserialize({
                    let res = ::alloc::fmt::format(::core::fmt::Arguments::new_v1(
                        &["Unknown variant_index "],
                        &[::core::fmt::ArgumentV1::new_display(&x)],
                    ));
                    res
                }))
            }
        }
    }
}
impl EnumB {
    fn def_BBB(&self, src: Version) -> String {
        String::new()
    }
}
codecov[bot] commented 1 year ago

Codecov Report

Merging #279 (6550b60) into main (8a6d318) will increase coverage by 0.35%. The diff coverage is 96.03%.

@@            Coverage Diff             @@
##             main     #279      +/-   ##
==========================================
+ Coverage   90.44%   90.79%   +0.35%     
==========================================
  Files          82       94      +12     
  Lines       24597    26260    +1663     
==========================================
+ Hits        22246    23843    +1597     
- Misses       2351     2417      +66     
Flag Coverage Δ
dbs-address-space 95.30% <ø> (ø)
dbs-allocator 94.98% <ø> (ø)
dbs-arch 96.37% <ø> (ø)
dbs-boot 94.90% <ø> (ø)
dbs-device 92.95% <ø> (ø)
dbs-interrupt 90.42% <ø> (ø)
dbs-legacy-devices 92.77% <ø> (ø)
dbs-miniball ∅ <ø> (∅)
dbs-upcall 94.31% <ø> (ø)
dbs-utils 91.25% <ø> (ø)
dbs-versionize 96.03% <96.03%> (?)
dbs-virtio-devices 87.34% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
crates/dbs-versionize/test-data/src/lib.rs 20.00% <20.00%> (ø)
crates/dbs-versionize/src/version_map.rs 83.87% <83.87%> (ø)
crates/dbs-versionize/src/lib.rs 84.90% <84.90%> (ø)
...s/dbs-versionize-derive/src/fields/struct_field.rs 87.17% <87.17%> (ø)
crates/dbs-versionize-derive/src/lib.rs 88.57% <88.57%> (ø)
crates/dbs-versionize-derive/src/common.rs 95.00% <95.00%> (ø)
...s/dbs-versionize-derive/src/fields/enum_variant.rs 97.19% <97.19%> (ø)
crates/dbs-versionize-derive/src/helpers.rs 97.96% <97.96%> (ø)
crates/dbs-versionize/src/primitives.rs 98.16% <98.16%> (ø)
...s-versionize-derive/src/descriptors/struct_desc.rs 99.00% <99.00%> (ø)
... and 2 more

:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more

ZizhengBian commented 1 year ago

Great job! I love this solution!

ZizhengBian commented 1 year ago

But how about the major version? If major version is different, you will deny to do serde?

wllenyj commented 1 year ago

But how about the major version? If major version is different, you will deny to do serde?

It is not handled here now, process in the snapshot crate, continue to use VersionManager to manage versions that can be upgraded.

studychao commented 1 year ago

@wllenyj We will migrate all our crates to Kata Containers repo. I will close this one now. And we will help you follow up this PR. Thank you very much.