# Metadata structure version and caching

Location: [gRPC API](https://developer.m-files.com/APIs/gRPC-API/index.md) > [Concepts](https://developer.m-files.com/APIs/gRPC-API/index.md#concepts)

HTML version: https://developer.m-files.com/APIs/gRPC-API/concepts/metadata-structure-version-and-caching/

Almost every client needs the metadata structure: object types, classes, property definitions,
workflows. It changes rarely, so read it once and cache it. The server tells you when to drop
the cache: the vault's **metadata structure version** changes whenever the structure changes.

## The metadata structure version

The metadata structure version is a 32-bit counter. It reaches you in two ways:

- **In the trailer of most calls.** Methods with `returns_metadata_structure_version` in their
  `(mfiles.api.method)` options (about 260 of the published methods, including
  [SearchObjects](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCSearch/SearchObjects/index.md), the object operations and the
  structure reads) send the trailing metadata `mfiles-metadata-version-bin`: 4 bytes, a
  little-endian signed 32-bit integer. The Sample Vault sent `00 04 00 00`, which is 1024.
- **On request.**
  [GetMetadataStructureVersion](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCVault/GetMetadataStructureVersion/index.md) returns it in
  `metadata_structure_version` (1024 in the Sample Vault, the same value as the trailer), plus
  `property_defs_version`, a separate counter for property definitions. This call itself sends no
  trailer.

gRPC libraries expose the trailer after the call completes, as the call's trailing metadata (in
Python, call the method with `with_call()`; the second return value has `trailing_metadata()`).
Most libraries hand you `-bin` values already base64-decoded, as raw bytes. A failed call sends
no metadata structure version trailer. [Call metadata](https://developer.m-files.com/APIs/gRPC-API/runtime/call-metadata/index.md) lists all metadata keys.

The server increments the version when the structure changes, for example when an administrator
changes object types or value list definitions, property definitions, classes and class groups,
workflows, named ACLs or languages. It does **not** change when objects change or when value
list items are added: adding an item to the Countries list left it at 1024.

## Finer-grained versions

Some reads have their own version number, which lets you skip unchanged data entirely:

| Method | Request field | Response field |
| --- | --- | --- |
| [GetPropertyDefs](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCPropertyDefs/GetPropertyDefs/index.md) | `known_version` | `current_version` |
| [GetObjectClassesAndGroups](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCPropertyDefs/GetObjectClassesAndGroups/index.md) | `known_version` | `current_version` |
| [GetValueListItemsWithPermissions](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCValueLists/GetValueListItemsWithPermissions/index.md) | `known_version`, `allow_delta` | `current_version`, `is_delta`, `removed_items` |

Send the `current_version` you received last time as `known_version`. If nothing has changed,
the response repeats the same `current_version` and contains **no items**: an empty list here
means "unchanged", not "empty". In the Sample Vault, `GetPropertyDefs` with `known_version` 483
returned 0 property definitions; Countries with `known_version` 17 returned 0 items, and after
an item was added, `allow_delta` true returned just that item with `is_delta` true and version
18. Value list items are not covered by the metadata structure version, so this is how to keep a cached
value list current.

## What to cache

The metadata structure version tracks the structure only, not object data. Cache by these rules:

| Cache until the metadata structure version changes | Do not cache on the metadata structure version |
| --- | --- |
| [GetObjectTypes](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectTypes/GetObjectTypes/index.md), [GetValueLists](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCValueLists/GetValueLists/index.md) | object reads: properties, files, versions, searches |
| [GetPropertyDefs](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCPropertyDefs/GetPropertyDefs/index.md), [GetObjectClassesAndGroups](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCPropertyDefs/GetObjectClassesAndGroups/index.md) | value list items (use their own version) |
| [GetWorkflowsForClient](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/GetWorkflowsForClient/index.md), [GetWorkflowStatesAndTransitions](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCObjectOperations/GetWorkflowStatesAndTransitions/index.md) without `obj_ver` | anything that writes |
| [GetNamedACLs](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCNamedACLs/GetNamedACLs/index.md) | permissions of objects |
| [GetMetadataStructureItemIdByAlias](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCVault/GetMetadataStructureItemIdByAlias/index.md), [GetMetadataStructureItemIdByGUID](https://developer.m-files.com/APIs/gRPC-API/Reference/MFiles.IRPCVault/GetMetadataStructureItemIdByGUID/index.md) | |

## A safe structure cache

```text
start:        v = GetMetadataStructureVersion().metadata_structure_version
              load the structure you need; store it under key (vault, user, language, v)
every call:   if the trailer mfiles-metadata-version-bin is present and differs from v:
                  drop the cached structure; set v to the new value; reload on next use
value lists:  refresh with known_version / allow_delta when you need current items
```

Key the cache by user and language, not only by vault: structure responses carry the caller's
permissions (for example `ObjectClass.current_user_permissions`) and names in the session's
language. A cache shared between users can show one user's view of the structure to another.

Hold IDs that you resolved from aliases or GUIDs in the same cache. They change only with the
structure, so a changed metadata structure version is also the signal to resolve them again.
