SchemaView - light schema access layer#9151
Conversation
|
When would users want to use this compared to the full fledged schema context? |
I was hoping for the answer to be "always" unless they are trying to store/author schemas. |
grigasp
left a comment
There was a problem hiding this comment.
Overall looks good.
A few comments:
-
Not sure the prefix
Runtimefits the purpose of the API. Based on the docs, it sounds likeSchemaContextis reserved for editing use cases, but that happens during runtime as well. -
I think a better distinction between
schemaContextandgetSchemas()onIModelConnectionandIModelDbis needed. Why one is a getter and the other is a method?The
schemaContextgetter is still marked@beta, so I think we could at least rename it. However, considering it becomes a niche API used only for specific situations, I would consider removing it completely and instead exposing a global factory function to create aSchemaContextwhen it's needed. -
Since
RuntimeSchemaContextis "lossy", I think the docs should be very clear with recommendations on what to do when that lost information is needed.For example, in my case I used
SchemaContextAPI to traverse fromProperty->KindOfQuantity->Format->Unit. With the new API,KindOfQuantityonly has presentation formats string. What do I do from there? -
I think we'll want to bump
IModelReadRpcInterface.versionto ensure frontends can use the new pragma.
Fair point - "Runtime" is ambiguous since The distinction we're drawing is "optimized for runtime consumption" (pre-loaded, read-only, synchronous, compact) vs "optimized for schema introspection and editing" (lazy-loaded, mutable, full-fidelity). "Runtime" is imperfect, changing it now would be a substantial update.
I think keeping it as a getter is the right call here.
For now you would have to use ECSql to pull that remaining info on demand. Let's dive into that and better underatand what we need. Modelling the whole units object model into this seems overkill to me. But I agree, if you DO need it, the path should be simple. Maybe RuntimeProperty could expose a helper method that does the work for you?
done |
I would consider
Personally, a getter vs a method wouldn't make me realize that one is more expensive than the other. FWIW, I would probably think a getter is cheaper. However, I'd like to reiterate on my suggestion to remove
I don't think we'll be able to stop looking at schema-based formats/units anytime soon. Even when the full replacement is ready, we'll probably want to look at what's in schema as a fallback. It doesn't necessarily have to be simple, but it has to be clear. My preference would be for // koq format spec:
// f:DefaultRealU(4)[u:M_PER_SEC_SQ];f:DefaultRealU(4)[u:CM_PER_SEC_SQ];f:DefaultRealU(4)[u:FT_PER_SEC_SQ]
// formats array:
[
{ formatName: "Formats.DefaultRealU", precision: 4, unitName: "Units.M_PER_SEC_SQ" },
{ formatName: "Formats.DefaultRealU", precision: 4, unitName: "Units.CM_PER_SEC_SQ" },
{ formatName: "Formats.DefaultRealU", precision: 4, unitName: "Units.FT_PER_SEC_SQ" },
]That way we could use |
I agree, format set fotmatsProvider already has the concept of a fallback provider to lean into, a schema formats provider naturally fits that. If need be, we can extend the formatsProvider interface to allow an optional fallback formatsProvider within, bringing that functionality out of FSFormatsProvider. And do something similar with unitsProvider and a fallback of some sort. |
There was a problem hiding this comment.
I looked over the public API surface and how SchemaView is maintained by and exposed through IModelDb and IModelConnection - LGTM. The only thing I don't really like is that now we'll have schemaContext getter + getSchemaView() method, but it's just my opinion, I understand the unwillingness to break the API.
I didn't look at at the tests or internals of SchemaView implementation. Hopefully someone from the iModels / EC team can do that.
…cess - Added RuntimeSchemaInterfaces.ts to define runtime schema-related types and enums. - Enhanced IModelConnection to support fetching runtime schema metadata with getRuntimeSchemas() method. - Updated ECSQL tutorial and reference documentation to include RuntimeSchemaContext as a performance alternative. - Created RuntimeSchemaContext.md to document its usage, advantages, and comparison with ecschema-metadata. - Added comprehensive tests for RuntimeSchemaContext functionalities, including schema navigation, class type checks, property handling, and exhaustive walks.
…ized schema metadata access - Added RuntimeSchemaContext for high-performance, read-only schema metadata caching. - Introduced RuntimeSchemaInterfaces to define data structures for schemas, classes, properties, and enumerations. - Updated ecschema-metadata module exports to include new runtime schema classes and interfaces. - Refactored IModelConnection to utilize RuntimeSchemaContext instead of the previous common module. - Updated documentation to reflect the new location of RuntimeSchemaContext and its usage. - Modified example tests to import RuntimeSchemaContext from the new module.
…RuntimeSchemaBinaryReader
…Context, clarifying behavior and performance considerations
…en support - Updated RuntimeClass to use a factory function for creating RuntimeProperty instances. - Refactored RuntimeProperty to be an abstract class with type-safe accessors for different property kinds. - Introduced new classes for RuntimePrimitiveProperty, RuntimePrimitiveArrayProperty, RuntimeStructProperty, RuntimeStructArrayProperty, and RuntimeNavigationProperty. - Added schemaToken support in RuntimeSchemaContext for cache invalidation and tracking context validity. - Modified parseRuntimeSchemaBlob to accept an optional schemaToken parameter. - Updated IModelConnection to pass schemaToken when creating RuntimeSchemaContext from binary data.
- Added methods to find enumerations, KindOfQuantities, and PropertyCategories by qualified name in RuntimeSchemaContext. - Refactored findView method to utilize a new internal helper for resolving schema items. - Introduced comprehensive tests for RuntimeSchemaContext, covering schema creation, property visibility, and view lookups. - Updated documentation to reflect the current state of view support in the runtime schema. - Adjusted example code snippets to align with the new method signatures and property checks.
- Added ecInstanceId property to RuntimeSchema, RuntimeClass, RuntimeProperty, RuntimeEnumeration, RuntimeKoQ, RuntimePropertyCategory, and RuntimeView classes to facilitate ECDbMeta queries. - Updated RuntimeSchemaContext and RuntimeSchemaBinaryReader to handle ecInstanceId during schema parsing and property resolution. - Modified IModelDb to improve schema hydration logic, ensuring concurrent callers share the same promise for schema updates. - Enhanced tests to validate ecInstanceId values against the corresponding database rows for schemas, classes, properties, enumerations, and categories. - Updated documentation to reflect the inclusion of ECViews in the runtime schema blob.
- Removed all references to views in the runtime schema, including their definitions and handling in the schema context. - Updated RuntimeClass to handle only entity and relationship classes, with type checks adjusted accordingly. - Refactored methods to use createRuntimeClass for instantiation, ensuring proper handling of relationship classes. - Updated tests to reflect changes in class handling and removed any assertions related to views. - Adjusted documentation to clarify the removal of views and the focus on entity and relationship classes.
- Updated `RuntimeSchemaInterfaces.ts` to change the import order of StrengthType and StrengthDirection, and modified arrayMinOccurs and arrayMaxOccurs to be optional. - Enhanced `RuntimeSchema.test.ts` by introducing new helper functions for writing schema, class, and property definitions, and refactored existing tests to utilize these helpers for better readability and maintainability. - Added a new documentation file `RuntimeSchemaBinaryFormat.md` detailing the binary format used for runtime schemas, including version history, table structures, and cross-reference resolution. - Updated `RuntimeSchemaContext.md` to include a reference to the new binary format documentation and clarified the differences between `ecschema-metadata` and `RuntimeSchemaContext`.
…ed schemas and their handling
…untimeSchemaContext
…escription and usage examples
- Update `isHidden` and `isEffectivelyHidden` properties to support tri-state logic for class visibility. - Modify binary parsing to accommodate new hidden state representation. - Revise documentation to clarify hidden attribute behavior and encoding.
6c19dde to
9740173
Compare
…erformance - Renamed RuntimePrimitiveType to SchemaViewPrimitiveType for better context in SchemaView. - Updated comments and documentation to reflect the new naming and clarify the purpose of the SchemaView binary format. - Enhanced error handling in BinaryReader to provide more informative messages when reading from the SchemaView blob. - Added unit tests for SchemaView parsing and error scenarios to ensure robustness. - Removed outdated documentation related to the previous runtime schema binary format. - Updated IModelConnection to handle schema fetching promises more safely, preventing potential race conditions.
…larification comment in IModelDb
…nge and rush extract-api
…ear to have zero consumers.
…injs-core into rschili/runtime-schema
…clarify usage for runtime read-only access and schema authoring.
imodel-native: iTwin/imodel-native#1369
Some diagrams refer to this as "Runtime Schemas" which was my working title. I didn't update all the pictures, just be aware. The final name we're going with is SchemaView.
Motivation
Yet another ECSchema layer for typescript, so let me explain:
SchemaContext) is the full-fidelity schema toolkit. It models every detail of the EC specification, but loading it is expensive: one synchronous RPC per schema, large memory footprint (~150-200 MB for a large iModel), and it blocks the backend event loop during loading which is the primary concern here.This PR introduces an optimized schema bundle which is transported via a single async call and parsed into an in-memory cache that provides fast synchronous access. It is lossy by design - it drops what runtime consumers do not need (units, formats, phenomena, full custom attribute instances) to keep the payload small and parsing fast.
Why one single bundle?
I did extensive testing. Of course it could be broken into smaller chunks, lazy loaded, but we have done all of that before - every approach has tradeoffs. This is keeping it simple and in testing performs rather well, even in scnearios in which it is weak by design.
The most extreme example is a single "check if class A IS of class B" on an iModel with huge schemas, magnitudes larger than an average iModel.
In this example, SchemaView takes about a .8 seconds spinup delay, then it responds instantaneously.
Asking the question directly via ECSql takes about 0.002s. With smaller iModels SchemaView catches up faster - ECSql is always available as an alternative option.
Design
Transport: A new
PRAGMA schema_view(N)in ECDb returns the binary blob via ConcurrentQuery. No new RPC methods - it flows through the existingqueryRowspath. The pragma accepts a format version parameter for forward compatibility.Binary format: C++ writer reads
ec_tables directly and produces a compact blob with:ecInstanceId(row ID fromec_tables) for fallback to ECSQL when neededCache invalidation: Uses
PRAGMA checksum(ecdb_schema)(SHA3-256) to detect schema changes. Schema imports or changeset pulls that modify schemas automatically invalidate the cache.Performance characteristics
Initial investigation into how much metadata we ingest, tested on an iModel with enormous metadata and poor performance:
Performance comparison walking all properties:
Performance comparison on simulated frontend with limited network bandwidth:
Memory footprint at runtime: