|
| 1 | +<!-- markdownlint-disable no-inline-html line-length --> |
| 2 | +<!-- markdownlint-disable-next-line first-line-heading --> |
| 3 | +<div align="center"> |
| 4 | +<p align="center"> |
| 5 | + |
| 6 | +<img alt="Voxel51 Logo" src="https://user-images.githubusercontent.com/25985824/106288517-2422e000-6216-11eb-871d-26ad2e7b1e59.png" height="55px"> |
| 7 | +<img alt="Voxel51 FiftyOne" src="https://user-images.githubusercontent.com/25985824/106288518-24bb7680-6216-11eb-8f10-60052c519586.png" height="50px"> |
| 8 | + |
| 9 | +</p> |
| 10 | +</div> |
| 11 | +<!-- markdownlint-enable no-inline-html line-length --> |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +# Configuring Multimodal Datasets |
| 16 | + |
| 17 | +<!-- toc --> |
| 18 | + |
| 19 | +- [Overview](#overview) |
| 20 | +- [Enabling the `VFF_MULTIMODAL` Feature Flag](#enabling-the-vff_multimodal-feature-flag) |
| 21 | + - [Workloads That Require The Flag](#workloads-that-require-the-flag) |
| 22 | +- [Delegated Operator Storage Requirements](#delegated-operator-storage-requirements) |
| 23 | + - [Why Multimodal Needs Extra Scratch Space](#why-multimodal-needs-extra-scratch-space) |
| 24 | + - [Minimum Recommended Sizing](#minimum-recommended-sizing) |
| 25 | + - [Example Configuration](#example-configuration) |
| 26 | +- [Pinning Projection Processing With `FIFTYONE_PROJECTION_DELEGATION_TARGET`](#pinning-projection-processing-with-fiftyone_projection_delegation_target) |
| 27 | + - [Behavior When Set](#behavior-when-set) |
| 28 | + - [Example Configuration](#example-configuration-1) |
| 29 | + |
| 30 | +<!-- tocstop --> |
| 31 | + |
| 32 | +## Overview |
| 33 | + |
| 34 | +FiftyOne Enterprise's multimodal datasets store large, non-sample-centric |
| 35 | +modalities (e.g. sensor streams, point clouds, telemetry) as Parquet-backed |
| 36 | +Iceberg tables rather than as ordinary Mongo-backed samples. |
| 37 | +A background delegated-operator pipeline (`run_projections` followed by |
| 38 | +`compact_projections`) continuously ingests new data and periodically |
| 39 | +compacts it into larger, size-bounded files. |
| 40 | + |
| 41 | +Running multimodal datasets requires three pieces of configuration beyond a |
| 42 | +standard install: |
| 43 | + |
| 44 | +1. The `VFF_MULTIMODAL` feature flag, set on every workload that serves or |
| 45 | + processes multimodal data. |
| 46 | +1. Enough ephemeral/scratch storage on delegated-operator workloads for |
| 47 | + projection compaction to succeed. |
| 48 | +1. Optionally, `FIFTYONE_PROJECTION_DELEGATION_TARGET` to pin projection |
| 49 | + processing to a specific orchestrator instead of relying on automatic |
| 50 | + selection. |
| 51 | + |
| 52 | +## Enabling the `VFF_MULTIMODAL` Feature Flag |
| 53 | + |
| 54 | +`VFF_MULTIMODAL` is a presence-only feature flag: setting it to any value |
| 55 | +(e.g. `1`) enables the feature. |
| 56 | +Multimodal support is off by default. |
| 57 | + |
| 58 | +### Workloads That Require The Flag |
| 59 | + |
| 60 | +| Workload | Helm values path | Why it's needed | |
| 61 | +| --- | --- | --- | |
| 62 | +| `teams-api` | `apiSettings.env` | Runs the periodic background task that queues projection delegated operations for datasets with pending data. | |
| 63 | +| `fiftyone-app` | `appSettings.env` | Serves the GraphQL/REST routes and grid queries that read multimodal Parquet data; without the flag, requests against a multimodal dataset are rejected. | |
| 64 | +| Delegated operator workloads | `delegatedOperatorDeployments.template.env` and/or `delegatedOperatorJobTemplates.template.env` | Runs `run_projections`/`compact_projections`, which write multimodal dataset metadata and raise an error if the flag isn't enabled. | |
| 65 | +| `teams-app` (frontend) | `teamsAppSettings.env` | Gates rendering of multimodal-specific UI components. | |
| 66 | +| `teams-plugins` | `pluginsSettings.env` | Serves operator schema/input resolution for the projection pipeline; recommended for consistency even though projection execution itself only ever runs on delegated-operator workloads. | |
| 67 | + |
| 68 | +> [!NOTE] |
| 69 | +> Set the flag on **every** workload in the table above. A partial |
| 70 | +> configuration (e.g. only `fiftyone-app`) leaves the UI able to query |
| 71 | +> multimodal data while ingestion silently fails, or vice versa. |
| 72 | +
|
| 73 | +## Delegated Operator Storage Requirements |
| 74 | + |
| 75 | +### Why Multimodal Needs Extra Scratch Space |
| 76 | + |
| 77 | +`compact_projections` reads all not-yet-compacted Parquet files for a |
| 78 | +projection table, merges/sorts them, and writes back a consolidated file, up |
| 79 | +to a configurable target size (1 GiB by default). For cloud warehouse |
| 80 | +locations (`gs://`, `s3://`, `az://`), both the download of source files and |
| 81 | +the write of the compacted output stage through the pod's local `/tmp` |
| 82 | +before being uploaded. |
| 83 | + |
| 84 | +If your delegated-operator workloads run with `securityContext.readOnlyRootFilesystem: true` |
| 85 | +(recommended for Pod Security Admission `restricted` compliance), `/tmp` |
| 86 | +is not writable at all unless you explicitly mount a writable volume there. |
| 87 | +An `emptyDir` volume mounted at `/tmp` without its own `sizeLimit` draws |
| 88 | +from the pod's overall `ephemeral-storage` resource budget; an `emptyDir` |
| 89 | +with an explicit `sizeLimit` is capped separately, on top of that shared |
| 90 | +budget. A `sizeLimit` that's too small for the amount of unconsolidated |
| 91 | +data will cause compaction to be evicted or fail before it can complete, |
| 92 | +even if the pod's aggregate `ephemeral-storage` limit is otherwise generous. |
| 93 | + |
| 94 | +### Minimum Recommended Sizing |
| 95 | + |
| 96 | +For any `delegatedOperatorDeployments`/`delegatedOperatorJobTemplates` |
| 97 | +instance that will run the projection pipeline: |
| 98 | + |
| 99 | +- `resources.limits`/`requests.ephemeral-storage`: at least `1.5Gi` |
| 100 | +- A `tmpdir` `emptyDir` mounted at `/tmp` with `sizeLimit: 1Gi` |
| 101 | + |
| 102 | +These are minimums for typical projection volumes, not a hard guarantee — |
| 103 | +size up further if your projections accumulate a large backlog of |
| 104 | +unconsolidated data between compaction runs (for example, if compaction has |
| 105 | +been disabled or failing for a period of time). |
| 106 | + |
| 107 | +### Example Configuration |
| 108 | + |
| 109 | +```yaml |
| 110 | +delegatedOperatorDeployments: |
| 111 | + template: |
| 112 | + env: |
| 113 | + VFF_MULTIMODAL: 1 |
| 114 | + resources: |
| 115 | + limits: |
| 116 | + ephemeral-storage: 1.5Gi |
| 117 | + requests: |
| 118 | + ephemeral-storage: 1.5Gi |
| 119 | + securityContext: |
| 120 | + readOnlyRootFilesystem: true |
| 121 | + volumeMounts: |
| 122 | + - name: tmpdir |
| 123 | + mountPath: /tmp |
| 124 | + volumes: |
| 125 | + - name: tmpdir |
| 126 | + emptyDir: |
| 127 | + sizeLimit: 1Gi |
| 128 | +``` |
| 129 | +
|
| 130 | +Apply the equivalent `env`, `resources`, `volumeMounts`, and `volumes` |
| 131 | +settings under `delegatedOperatorJobTemplates.template` if you also use |
| 132 | +on-demand (Kubernetes `Job`-based) delegated operators for projection |
| 133 | +processing. |
| 134 | + |
| 135 | +> [!NOTE] |
| 136 | +> If your delegated-operator workloads do **not** set |
| 137 | +> `readOnlyRootFilesystem: true`, `/tmp` is already part of the container's |
| 138 | +> writable filesystem and draws directly from the pod's `ephemeral-storage` |
| 139 | +> limit with no separate cap — you only need the explicit `tmpdir` volume |
| 140 | +> and `sizeLimit` when the root filesystem is read-only. |
| 141 | + |
| 142 | +## Pinning Projection Processing With `FIFTYONE_PROJECTION_DELEGATION_TARGET` |
| 143 | + |
| 144 | +By default, `teams-api` automatically selects the lowest-compute active |
| 145 | +orchestrator (excluding GPU orchestrators) to run each dataset's projection |
| 146 | +pipeline. Set `FIFTYONE_PROJECTION_DELEGATION_TARGET` on `apiSettings.env` |
| 147 | +to pin all projection processing to one specific orchestrator instead — |
| 148 | +for example, to ensure it always lands on the orchestrator instance sized |
| 149 | +for compaction per the previous section. |
| 150 | + |
| 151 | +The value must be the exact registered name of an always-on delegated |
| 152 | +operator instance — i.e. the kebab-cased key under |
| 153 | +`delegatedOperatorDeployments.deployments.<key>` (the same name each |
| 154 | +instance registers with via `fiftyone delegated launch -n <name>`). |
| 155 | +On-demand (`delegatedOperatorJobTemplates`) instances cannot be used as a |
| 156 | +delegation target, since they don't run as a persistent, named orchestrator. |
| 157 | + |
| 158 | +### Behavior When Set |
| 159 | + |
| 160 | +There is **no fallback to automatic selection**. If the configured value |
| 161 | +does not match an active orchestrator capable of running the projection |
| 162 | +operator, `teams-api` logs an error and skips queuing any projection |
| 163 | +delegated operations that cycle — pending datasets simply won't be |
| 164 | +processed until the value is corrected or removed. |
| 165 | + |
| 166 | +### Example Configuration |
| 167 | + |
| 168 | +```yaml |
| 169 | +delegatedOperatorDeployments: |
| 170 | + deployments: |
| 171 | + teamsDoMultimodal: |
| 172 | + # ... sized per "Delegated Operator Storage Requirements" above |
| 173 | +
|
| 174 | +apiSettings: |
| 175 | + env: |
| 176 | + FIFTYONE_PROJECTION_DELEGATION_TARGET: teams-do-multimodal |
| 177 | +``` |
0 commit comments