Skip to content

perf: Replace MessageToDict with optimized custom dict builder#6015

Merged
franciscojavierarceo merged 12 commits into
feast-dev:masterfrom
abhijeet-dhumal:fix/custom-json-serialization-6013
May 28, 2026
Merged

perf: Replace MessageToDict with optimized custom dict builder#6015
franciscojavierarceo merged 12 commits into
feast-dev:masterfrom
abhijeet-dhumal:fix/custom-json-serialization-6013

Conversation

@abhijeet-dhumal
Copy link
Copy Markdown
Contributor

@abhijeet-dhumal abhijeet-dhumal commented Feb 24, 2026

Summary

Fixes #6013

The feature server uses google.protobuf.json_format.MessageToDict for converting
GetOnlineFeaturesResponse to JSON, but this generic converter is slow due to
reflection-based field introspection.

Changes

  • Add feature_server_utils.py with convert_response_to_dict() function
  • Custom dict builder optimized for GetOnlineFeaturesResponse structure
  • Direct field access instead of reflection
  • Efficient type-specific value extraction
  • Serialize top-level status bool field (omitted when false, matching proto3 default behavior)
  • Add comprehensive unit tests for the new serialization

Performance Impact

~4x faster serialization, saving 5-15ms per request for typical feature responses
with 50-200 features.

Test Plan

  • Added unit tests in test_feature_server_utils.py
  • Tests verify output matches MessageToDict + proto_json.patch() format
  • Tests cover all Value types, timestamps, metadata, status codes, top-level status field
  • Performance test validates speedup
  • Passes make lint-python

@abhijeet-dhumal abhijeet-dhumal requested a review from a team as a code owner February 24, 2026 06:27
devin-ai-integration[bot]

This comment was marked as resolved.

@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch 2 times, most recently from 6a38f3c to 2b81774 Compare February 24, 2026 06:36
devin-ai-integration[bot]

This comment was marked as resolved.

@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from 2b81774 to 8fbda9f Compare February 24, 2026 06:44
devin-ai-integration[bot]

This comment was marked as resolved.

@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from 8fbda9f to f3ea953 Compare February 24, 2026 06:52
devin-ai-integration[bot]

This comment was marked as resolved.

@ntkathole ntkathole changed the title [perf] Replace MessageToDict with optimized custom dict builder perf: Replace MessageToDict with optimized custom dict builder Feb 25, 2026
@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from 06877fd to 063eeb2 Compare February 25, 2026 14:19
@abhijeet-dhumal abhijeet-dhumal marked this pull request as draft February 25, 2026 14:20
@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch 2 times, most recently from d3c512d to c06a75d Compare February 25, 2026 14:22
devin-ai-integration[bot]

This comment was marked as resolved.

@abhijeet-dhumal abhijeet-dhumal marked this pull request as ready for review February 25, 2026 14:31
devin-ai-integration[bot]

This comment was marked as resolved.

@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from c06a75d to 53a5264 Compare March 2, 2026 05:56
devin-ai-integration[bot]

This comment was marked as resolved.

@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from 2616fb8 to 300be07 Compare March 2, 2026 06:33
@ntkathole ntkathole force-pushed the fix/custom-json-serialization-6013 branch from 300be07 to a8c7653 Compare March 5, 2026 04:55
}


def response_to_dict_fast(response: GetOnlineFeaturesResponse) -> Dict[str, Any]:
Copy link
Copy Markdown
Member

@franciscojavierarceo franciscojavierarceo Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not response_to_dict? better yet, convert_response_to_dict is a better description

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - renamed to convert_response_to_dict. The _fast suffix was leaking implementation details ✅

@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from a8c7653 to 9859bad Compare March 10, 2026 08:58
devin-ai-integration[bot]

This comment was marked as resolved.

@ntkathole ntkathole force-pushed the fix/custom-json-serialization-6013 branch from bd8f618 to eb7547c Compare March 10, 2026 14:57
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 9 additional findings in Devin Review.

Open in Devin Review

Comment thread sdk/python/feast/feature_server_utils.py
@franciscojavierarceo
Copy link
Copy Markdown
Member

@abhijeet-dhumal please resolve conflicts and update PR

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
…nt FieldValues

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
… and precision, add base64 encoding for bytes

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from eb7547c to 0c0637b Compare May 27, 2026 11:12
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment thread sdk/python/feast/feature_server.py Outdated
Comment on lines +62 to +68
which = v.WhichOneof("val")
if which is None or which == "null_val":
return None
elif "_list_" in which:
return list(getattr(v, which).val)
else:
return getattr(v, which)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 bytes_val values cause ORJSONResponse to raise TypeError

_value_to_native at feature_server_utils.py:68 returns raw bytes for bytes_val fields. Even if orjson were installed, orjson.dumps() cannot serialize Python bytes objects and raises TypeError: Type is not JSON serializable: bytes. The old code path returned the dict to FastAPI which processed it through jsonable_encoder (capable of handling bytes). The switch to ORJSONResponse bypasses that encoder, causing a regression for any response containing bytes-typed feature values.

Prompt for agents
In _value_to_native (feature_server_utils.py), when the value type is bytes_val, the raw bytes object is returned. orjson cannot serialize bytes. You need to either: (1) base64-encode bytes values before returning them (e.g. using base64.b64encode(val).decode('ascii')), or (2) not use ORJSONResponse and instead use a serializer that handles bytes. Note that bytes_list_val (handled by the _list_ branch) would also produce a list of bytes objects with the same issue.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abhijeet-dhumal bytes_val is still problem since you are using JSONResponse

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this, Nikhil! Now bytes_val and bytes_list_val now base64-encode the output before returning, so JSONResponse / json.dumps can serialize them without TypeError.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review!

Comment thread sdk/python/feast/feature_server_utils.py Outdated
@abhijeet-dhumal
Copy link
Copy Markdown
Contributor Author

@ntkathole Apologies for the late reply here .. I have fixed and addressed the open reviews!

Comment thread sdk/python/feast/feature_server.py Outdated
from fastapi.concurrency import run_in_threadpool
from fastapi.logger import logger
from fastapi.responses import JSONResponse
from fastapi.responses import JSONResponse, ORJSONResponse
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
Previous implementation used "_list_" substring check which silently
returned protobuf objects (not JSON-serializable) for:
- *_set_val types (bytes_set_val, string_set_val, int64_set_val, etc.)
- list_val / set_val (RepeatedValue with nested Values)
- map_val / struct_val (Map<string, Value>)
- map_list_val / struct_list_val (MapList)

Add tests for all previously uncovered types and a float32 precision
consistency test against MessageToDict.

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
…nt set-type behavior

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
@abhijeet-dhumal abhijeet-dhumal force-pushed the fix/custom-json-serialization-6013 branch from 8d76583 to b2f2909 Compare May 27, 2026 15:12
…p test

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
bytes_val returned raw bytes which cause json.dumps TypeError when
JSONResponse serializes the response. Base64-encode these values to
match standard protobuf JSON encoding for bytes fields.

Fixes Nikhil's review comment: bytes_val is still problem since you
are using JSONResponse.

Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
Signed-off-by: abhijeet-dhumal <abhijeetdhumal652@gmail.com>
Copy link
Copy Markdown
Member

@ntkathole ntkathole left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@franciscojavierarceo franciscojavierarceo merged commit 9902064 into feast-dev:master May 28, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[perf] Replace MessageToDict with optimized custom dict builder - 4x faster JSON serialization

3 participants