CLI: Stats command - fix incorrect CPU % reporting (#40627)
🎬 Episode 1 premieres on June 2 at 12 PM PT
🔗 Learn more: https://aka.ms/iq-series
Welcome to the Work IQ track within The IQ Series. In the Work IQ episodes, we explore how Work IQ powers AI agents by understanding organizational data, context, and work patterns.
The IQ Series is a developer‑focused learning series exploring Microsoft IQ as the intelligence layer for modern AI systems.
Work IQ is a workplace intelligence layer that delivers a semantic understanding of everything happening across your business. Built on four core components—Chat, Context, Tools, and Workspaces—it continuously transforms signals from across Microsoft 365 and business systems into agent‑ready intelligence, enabling work grounding, reasoning, and permission awareness. The result is a platform purpose-built for agentic use, delivering higher intelligence, speed, efficiency, enterprise scale, and security and governance by design.
The following is a detailed explanation of the background behind the new gRPC-Rust client API. It covers the process I went through while designing it, alternatives considered, trade-offs, and advantages of the final design. If you are only interested in the result itself, please feel free to skip straight to our documentation instead.
gRPC is a high performance Remote Procedure Call (RPC) framework. If you aren’t already familiar with gRPC, please see our introduction.
Tonic is an implementation of the gRPC protocol for the Rust programming language. It is widely used and very popular, with over 12k stars on github. Tonic is designed to be part of the Tower ecosystem, which is a framework that attempts to unify all networking client and service APIs, allowing easy introduction of common middleware for your application.
The gRPC-Rust project was started as an effort to bring all the advanced gRPC features missing from Tonic to the Rust community, like integrated health checking and retries, as well as performance-boosting strategies like zero-copy and arena optimizations.
Tonic is already used by a large number of Rust users, so my initial thought was that it would be great if we could re-use its API. Here’s a quick sample of the current Tonic client-side API:
// Simple unary call (one request, one response):
let response = client
.get_feature(Request::new(Point { latitude: 409146138, longitude: -746188906,}))
.await?;
// Bidirectional streaming (many requests and responses in parallel:
let outbound = async_stream::stream! { /* request stream generator */ };
let response = client.route_chat(Request::new(outbound)).await?;
let mut inbound = response.into_inner();
while let Some(note) = inbound.message().await? { /* process responses */ }
When analyzing this more closely, I discovered several issues and limitations:
GrpcService::ResponseBody is http_body::Body). gRPC applications should
not assume that gRPC is using HTTP/2 as a transport to enable the use of other
transports (e.g.
QUIC) in
the future.Stream from futures_stream (via
tokio_stream). These crates are unstable, and we want to be able to have a
stable release of gRPC-Rust before they are stabilized.? operator) to
propagate outgoing client call statuses to server responses, including
metadata, which could contain secrets like tokens or other private
information.So we’ll need to change the Tonic API, but should we keep using Tower?
In our other supported languages, gRPC directly includes many of the features
that you would get from Tower middleware, e.g.
timeouts and
retries. And, gRPC provides versions of
this functionality specifically designed to work well with gRPC. In addition,
Tower was not created with streaming in mind, even though it technically works.
To implement a bidirectional stream, the Request and Response become
asynchronous objects, and the call method returns early in the RPC’s lifecycle
to allow the application and library to interact with them.
To illustrate these problems, let’s look at timeouts as an example. The Tower
crate provides a timeout module
to provide this functionality. The approach is to race the Service it wraps
with a timer future, dropping one when the other completes. This works fine for
many Service implementations, but it does have some surprising behavior many
people are unaware of:
poll_ready,
meaning your call could block indefinitely.Response object to be returned from the call method -- which, for
streaming RPCs, will be almost immediately. In gRPC, timeouts apply to the
total time of the RPC from the moment the client starts attempting the call
to the receipt of the final status from the server.In addition to those behaviors which affect any Service, there is another
incompatibility with gRPC: we write the timeout on the wire so that the server
is aware of the client’s deadline. If an application was using the timeout
module, the gRPC library would never be aware of the timeout in order to
propagate it.
Another limitation of Tower is that it makes it hard for the application to
control memory allocations: the Response would need to be a complex type that
allows you to call into it to receive the message into a buffer. For streaming
RPCs, something like this would be needed anyway, but for unary RPCs, which is
where arenas are most important, it would be awkward.
If Tower is a poor fit, should we still keep the same style for calls that Tower and Tonic users are familiar with? I.e.
async fn call(Request) -> Response
We ultimately decided against this approach. With this style, applications that
wished to do interleaved operations (“send a message, receive a message, send a
message, …”) would need to deal with the two Request and Response streams
executing concurrently and implement their own synchronization between the two
streams.
gRPC actually provides two different APIs: one that the application typically interacts with via the protobuf generated code, and one that the channel (the main client entry point) itself implements, and that interceptors (AKA middleware) would use. The generated API focuses more on usability while the channel API is a lower-level, more powerful, streaming-only design.
In Tonic, this split exists as well, but the proto generated code is just a specialization of the Tonic API using type generics. But this approach is not a requirement, and in fact, there are reasons to avoid it.
With gRPC-Rust, we are taking the opportunity to incorporate some lessons learned from our experience implementing gRPC in many other languages. One such idea we’d like to incorporate is to hide the details of gRPC itself from the generated API, and only expose protobuf messages and necessary primitives. As an example:
// *Not* something like this:
async fn call(ctx: grpc::Context, req: MyRequestMessage, options: grpc::CallOptions)
-> grpc::Response<MyResponseMessage>;
// More like this instead:
async fn call(req: MyRequestMessage) -> Result<MyResponseMessage, Status>;
// Example usage:
let response = client.call(request).await.expect("RPC should succeed!");
This is similar to gRPC-Java’s design, and it allows applications to focus on the business logic of the application: requests and responses. For other functionality that gRPC provides -- like accessing metadata, disabling retry, reading peer details, etc -- these are generally things that interceptors should be used for, instead.
With that in mind, let’s dive into the generated (protobuf) API in isolation first, and understand why the nice, simple API in the example above isn’t good enough. (Then I’ll explain how we ultimately achieved exactly that anyway!)
As mentioned earlier, arenas are important for making highly efficient RPC systems that can handle extremely high QPS (Queries Per Second), by grouping related memory operations temporally and spatially. To allow the application to control allocations instead of the library, we were looking for a unary API something like:
// Definition:
async fn call(req: &MyRequestMessageView, resp: &mut MyResponseMessageView) -> Status;
// Usage (with a hypothetical arena API):
let req = MyRequestMessage::new_on_arena(arena).set_id(3);
let res = MyResponseMessage::new_on_arena(arena);
client.call(&req, &mut res).expect("RPC should succeed!");
// res now contains the RPC's response
That API is straightforward, but we understand that many users would not want to
pre-declare the response message type manually before making every RPC. We can
accommodate both use cases by using the async builder
pattern.
An async builder is able to return owned messages via an IntoFuture
implementation, while also providing an alternative method to perform the call
using a pre-allocated response.
We can also improve the ergonomics of the request message parameter. Instead of
requiring a reference, we can accept an
AsView
protobuf type that allows either an owned message or a view to be passed into
the call. This is the final API we have implemented:
// Definition:
async fn call<Req>(req: Req) -> UnaryFutureBuilder<..>
where
Req: AsView<Proxied=MyRequestMsg>;
// Implements the simple usage:
impl IntoFuture for UnaryFutureBuilder {
type Output = Result<MyResponseMessage, StatusError>;
}
// Implements the advanced usage method:
impl UnaryFutureBuilder {
async fn with_response_message<Res>(self, res: &mut Res) -> Status
where
Res: AsMut<MutProxied = MyResponseMessage>;
}
// Usage:
fn main() {
let request = proto!(MyRequestMessage{ id: 3 });
// Simple Usage -- exactly what we wanted originally!:
let response = client.call(request).await.expect("RPC should succeed!");
// Arena usage (again with a hypothetical arena API):
let req = MyRequestMessage::new_on_arena(arena).set_id(3);
let res = MyResponseMessage::new_on_arena(arena);
client.call(&req).with_response_message(&mut res).await.expect("RPC should succeed!");
}
We adapted these same concepts to our streaming APIs as well. Below is an example of the bidirectional streaming API:
// Definition:
async fn begin_stream() -> BidiCallBuilder<..>
// Implements the same async builder pattern:
impl IntoFuture for BidiCallBuilder<..> {
type Output = (GrpcStreamingRequest, GrpcStreamingResponse);
}
// Usage:
fn main() {
// Simple Usage:
let (request_stream, response_stream) = client.begin_stream().await;
request_stream.send(proto!(MyRequestMessage{..}));
let response = response_stream.recv().await.expect("RPC should succeed!");
// Arena usage:
let res = MyResponseMessage::new_on_arena(arena);
let response = response_stream.recv_into(&mut res).await.expect("RPC should succeed!");
}
Please see the full documentation for more detailed usage examples.
This covers the generated code API design process. In Part 2 I’ll go into further details on the channel APIs.
The Ember project is excited to announce the release of Ember v7.0. Following Ember's Major Version Policy, the major includes only the removal of features that were deprecated until 7.0 as well as other bugfixes. This release of Ember.js means the previous version, 6.12, is now an LTS (Long Term Support) version.
When it comes to introducing new features, Ember generally aims to ship new features in minor releases, offering backwards compatibility for existing code at the same time as giving developers the chance to try out new capabilities. This approach reduces the challenges that teams face for major upgrades, compared to producing big, breaking, splashy major versions with lots of new features.
In Ember 6.x minor releases, we landed many notable features:
ember-source as a v2 addon, which enabled Embroider to consume ember-source directly.Embroider and Vite-based build system as the default.strict-mode (aka template-tag) component authoring format as the default.renderComponent API that allows rendering individual components without a full project.trackedArray, trackedObject, trackedMap, trackedSet, trackedWeakMap, and trackedWeakSet.Ember v6.8 was the biggest change to how an Ember project is built in recent memory. The Ember Core Teams have been working on the Embroider+Vite build system and the strict-mode-templates (a.k.a template-tag) for literal years, and this is the first version that we made them the default experience for newly generated Ember apps. Early adopters have been opting into these features for quite some time, but generating an app with Ember v6.8 (or newer) now gives you an incredibly modern developer experience 🎉
Along with these features, countless bugfixes, deprecations that cleared the way for future improvements, RFCs setting the stage for new features, in 6.x the community also:
glimmer-vm monorepo into ember.js to faciliate faster iteration on the rendering engine and the integration with ember.js.router.js repo into the ember.js repo to prepare for experimentation with a new router architecture and the implementation of the Route Manager RFCOIDC.broccoli and other dependencies in an ongoing effort to reduce the number of security vulnerabilities. Current vulnerability warnings are only a concern in development mode and should not be exploitable but are annoying.Thank you to all the contributors that helped make this major-version cycle possible!
The most common approach for upgrading to 7.0 is to upgrade your app to the last version of Ember 6, which is 6.12, resolve all deprecation warnings, and then upgrade to 7.0. If your app or addon runs with no deprecations in the latest release of 6.12, you should be able to upgrade to Ember 7 with no additional changes. Step-by-step directions are below.
If your app is at a much earlier version in the 6.x series, we recommend upgrading in steps across the LTS versions: 6.4, 6.8, and then 6.12.
Follow these steps in order:
Consider upgrading addons used in your app to the latest version that you can. This will reduce the uses of deprecated APIs in your dependencies.
Upgrade your project to the latest patch version of Ember 6.12.
Many projects can do this by running
npx ember-cli-update --to 6.12.
When upgrading across the 6.8 boundary, due to changes in the build system, you need to adjust the configuration for ember-cli.
Make sure your app builds successfully.
Resolve all deprecation warnings. These Deprecated APIs are removed in Ember 7.0. You may need to upgrade some of your addon dependencies if they are using deprecated APIs. See the Ember Deprecation Guide for more details about specific deprecations and how to resolve them. Applications that need to upgrade through several versions may want to consider isolating individual deprecations by using ember-cli-deprecation-workflow.
Make sure your app builds successfully and your test suite passes with no deprecations.
Upgrade your app to Ember 7.0. Again,
many developers can do this by running
npx ember-cli-update --to 7.0.
For help or advice along the way, visit Ember's forums or chat groups.
Ember.js 7.0 introduces no new public API. Instead, it comprises bug fixes and the breakage of previously deprecated public API from the 6.x cycle.
Ember.js 7.0 introduced no new deprecations. It removed all deprecations that were introduced before 6.10 and slated for removal in 7.0.
Ember 7.0 removes the following features deprecated during 6.x:
import Ember from 'ember'. See the RFC or deprecations in your project for a guide on replacement APIs.ember-source. This can be opted-into before 7.0 by using an optional feature: see the deprecation guide for more details. This also requires certain dependency updates.inject from @ember/service. It is now import { service } from '@ember/service'.For more details on how to resolve these deprecations in your app, see the deprecations guide.
Ember.js 7.0 includes many bug fixes, the following are some of the more notable ones:
this in explicit scope for the runtime template compiler.LinkTo inside inline SVG reloads your applicationdelete() returning true for non-existent entries{{#each}} runtime crash when array contains null/undefined items with keytrackedMap and trackedWeakMap reactivity for existing keysBrandedArray fix for Array-as-parent bug in destroyablesEmberArray.reduce to match native behavior<LinkTo>'s @current-when argument with nested routes containing dynamic segmentsEmber CLI 7.0 introduces no new public API, adds no new deprecations, and adds no new bugfixes. This release only adds an automatic breakage to previously deprecated public API from the 6.x cycle. This code will be cleaned up in future releases. You can read more about the deprecations in the deprecation guide
As a community-driven open-source project with an ambitious scope, each of these releases serves as a reminder that the Ember project would not have been possible without your continued support. We are extremely grateful to our contributors for their efforts.
Today, the gRPC project is excited to share its first preview release of gRPC-Rust. It can be found at crates.io/crates/grpc. Documentation is available on our website, grpc.io. This release is not recommended for production use at this time, but is being released at an early stage to provide Rust programmers with an opportunity to experiment with the APIs and ensure they will be acceptable for their needs.
This release includes:
gRPC Client APIs. Includes all RPC types including unary and streaming.
Protobuf support using Google’s Protobuf-Rust library and tools.
protoc.protoc plugin.Low-level RPC API for creating interceptors and performing RPCs without the need for the protobuf generated code.
Support for the Tokio async runtime.
Documentation for this gRPC-Rust preview can be found on grpc.io. The following resources are available:
Please reach out to the team if you have any feedback or encounter any issues:
If you’re interested in meeting the gRPC team, discussing related topics with others in the industry, or maybe even sharing your own experiences or advice in a talk, please mark your calendar for Thursday, September 3rd, when we’ll be holding this year’s gRPC developer’s conference at the Computer History Museum in Mountain View, CA.
I pondered the other day what's next for Microsoft's Surface PC lineup, and it looks like we're about to find out. Windows and Surface chief Pavan Davuluri has just teased "something new is coming for developers," complete with a mysterious image of what looks like a curved display edge.
Davuluri notes that whatever is coming is "not a new OS version," so that rules out the potential for Windows 12 to be announced at Microsoft's Build developer conference next week. Separately, the Windows account on X posted a similar cryptic tweet, promising "a new era of PC" with coordinates pointing to where Computex is hosted in Taipei.