MEMO: Tonic 0.14 Upgrade Path
Context
During dependency consolidation (#76), we attempted to upgrade the Rust client gRPC stack from tonic 0.11/prost 0.12 to tonic 0.14/prost 0.14. The upgrade was deferred due to breaking API changes that require significant build system restructuring.
Current State
Stable Stack (as of 2024-01-24):
tonic = "0.11"prost = "0.12"tonic-build = "0.11"- Location:
clients/rust/prism-client/Cargo.toml
Build Configuration:
// clients/rust/prism-client/build.rs
tonic_build::configure()
.build_server(false)
.compile(&[...protos...], &[...includes...])?;
Breaking Changes in Tonic 0.14
1. API Redesign
Old API (0.11):
tonic_build::configure()
.build_server(false)
.compile(protos, includes)?;
New API (0.14):
configure()method removed- Replaced with
CodeGenBuilder+prost_build::Configintegration - No direct
compile_protos()function
2. Build System Architecture
Tonic 0.14 split responsibilities:
Code Generation: tonic_build::CodeGenBuilder
- Generates gRPC service client/server code
- Does NOT implement
prost_build::ServiceGeneratortrait - Only provides token generation methods
Proto Compilation: prost_build::Config
- Handles .proto file parsing and compilation
- Requires custom
ServiceGeneratorimplementation - Must manually wire together with
CodeGenBuilder
3. Manual Service Definition Required
The manual module now requires explicit service construction:
use tonic_build::manual::{Builder, Service, Method};
let service = Service::builder()
.name("MyService")
.package("my.package")
.method(Method::builder()
.name("my_method")
.input_type("MyRequest")
.output_type("MyResponse")
.build())
.build();
Builder::new()
.build_client(true)
.build_server(false)
.compile(&[service]);
Problem: This requires parsing .proto files manually or duplicating service definitions.
Migration Path
Phase 1: Research & Design (1-2 days)
-
Study tonic 0.14 examples:
- Review official tonic repository examples
- Check tonic-build documentation for migration guides
- Examine other projects that completed this migration
-
Design options:
- Option A: Use
prost_build::Configwith customServiceGenerator - Option B: Use
tonic_build::manual::Builderwith parsed proto metadata - Option C: Wait for tonic 0.15+ with simplified API
- Option A: Use
-
Prototype approach:
- Create test branch with single .proto file
- Verify generated code matches current output
- Validate client functionality
Phase 2: Implementation (2-3 days)
-
Update Cargo.toml:
[dependencies]
tonic = "0.14"
prost = "0.14"
[build-dependencies]
tonic-build = "0.14"
prost-build = "0.14" # Now required -
Rewrite build.rs:
Recommended Approach (Option A):
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut config = prost_build::Config::new();
// Create custom service generator wrapping CodeGenBuilder
struct TonicServiceGen {
builder: tonic_build::CodeGenBuilder,
}
impl prost_build::ServiceGenerator for TonicServiceGen {
fn generate(&mut self, service: prost_build::Service, buf: &mut String) {
// Convert prost Service to tonic Service
// Generate client code using builder
let code = self.builder.generate_client(&service, "");
buf.push_str(&code.to_string());
}
}
config.service_generator(Box::new(TonicServiceGen {
builder: tonic_build::CodeGenBuilder::new(),
}));
config.compile_protos(&[...], &[...])?;
Ok(())
} -
Handle generated code differences:
- Generated struct names may change
- Import paths may differ
- Client initialization API may vary
-
Update client code (
clients/rust/prism-client/src/):- Review all
tonic::imports - Update service client instantiation
- Verify request/response handling
- Review all
Phase 3: Testing & Validation (1-2 days)
-
Build verification:
cargo clean
cargo build
cargo test -
Integration testing:
- Run all Rust client examples
- Verify gRPC communication with proxy
- Test error handling paths
-
Performance validation:
- Compare request latency
- Check memory usage
- Verify no protocol regressions
Phase 4: Documentation (1 day)
-
Update client docs:
- Note API changes in RFC-040
- Update code examples
- Document new build requirements
-
Update changelog:
- Describe breaking changes
- List migration steps for users
Estimated Effort
Total: 5-8 days
- Research: 1-2 days
- Implementation: 2-3 days
- Testing: 1-2 days
- Documentation: 1 day
Risk: Medium
- Build system changes are isolated to build.rs
- Client code changes expected to be minimal
- Rollback is straightforward (revert Cargo.toml)
Alternative: Wait for Tonic 0.15+
Consideration: The tonic maintainers may simplify the API in future versions based on community feedback about 0.14's complexity.
Trade-offs:
- Pro: Potentially easier migration path
- Pro: More examples and documentation available
- Con: Delayed security/performance updates
- Con: Growing version drift with ecosystem
Recommendation: Monitor tonic releases quarterly. Upgrade when either:
- Tonic 0.15+ restores simpler API
- Critical security update requires tonic 0.14+
- New tonic 0.14 feature needed for Prism functionality
Dependencies Successfully Updated
These updates were completed in PR #76:
reqwest: 0.11.27 → 0.12.24thiserror: 1.0.69 → 2.0.17rand: 0.8.5 → 0.9.2 (dev-dependency)- All Go modules (grpc, protobuf)
- All GitHub Actions
- All npm packages
References
- Tonic 0.14 Release Notes
- Tonic Build Documentation
- Prost Build ServiceGenerator
- PR #76: Dependency consolidation (tonic deferred)
Next Steps
- Monitor: Watch tonic repository for 0.15 release or API improvements
- Document: Link this memo from RFC-040 (client SDK architecture)
- Schedule: Add "Tonic upgrade evaluation" to Q1 2025 technical debt review
- Alert: Create dependabot rule to notify on tonic 0.15+ releases