> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Concealed measures

> How a data cube of a distributed Atoti application conceals measures from its query cubes, which measures can use them, and which limits apply.

A data cube can conceal some of its measures from the query cubes of its application. A concealed measure stays computable on the data cube. It is never sent to the query cubes or executed there. Typical candidates are the intermediate steps of a calculation that end users never query. Only a post-processor whose execution is disabled in the query cube can use a concealed measure. This page explains how to conceal measures and how to define the measures that use them. It assumes familiarity with [distributed post-processors](https://docs.activeviam.com/engine/java-sdk/6.2/distributed/distributed_post_processors.md).

> [!NOTE]
>
> Concealed measures are available from Atoti Server 6.2.3.

## What is a concealed measure?

A data cube normally sends all its measures to the query cubes of its application. It can instead conceal some of them. A concealed measure stays fully computable on the data cube, but it is never sent to the query cubes, so a query cube can neither query it nor compute anything from it. A concealed measure will not appear in the list of queryable measures in any UI.

Concealing measures is independent of concealing hierarchies or branches. A data cube can use any combination of the three. See [Recommendations](https://docs.activeviam.com/engine/java-sdk/6.2/distributed/distributed_recommendations.md).

## How to conceal measures

Measures are concealed with the optional `withConcealedMeasures` step of the data cluster definition builder, available after `withAllHierarchies()` or `withConcealedBranches()`. It takes the measure names, or a `Collection<String>` of them. Calling it again replaces the measures concealed so far.

```java theme={"languages":{"custom":["/engine/python-sdk/6.2/languages/pycon.tmLanguage.json"]}}
.asDataCube()
.withClusterDefinition()
.withClusterId("SupplyChainCubeCluster")
.withMessengerDefinition()
.withProtocolPath("jgroups-protocols/protocol-tcp.xml")
.end()
.withApplicationId("supply-chain")
.withAllHierarchies()
.withConcealedMeasures("delay.v1", "delay.v2")
.end()
.build();
```

Applications that build the data cluster definition directly call `IDataClusterDefinition#setConcealedMeasures(Collection<String>)`. The concealed names are read back with `IDataClusterDefinition#getConcealedMeasures()`.

## Depending on a concealed measure as underlying measure

A query cube never receives a concealed measure. A measure that is not concealed but has a concealed measure among its underlying measures cannot be computed there. It must be a post-processor whose execution is disabled in the query cube, as described in [How to force the execution in the data cube](https://docs.activeviam.com/engine/java-sdk/6.2/distributed/distributed_post_processors.md#how-to-force-the-execution-in-the-data-cube). Otherwise, the data cube fails at startup with a `DescriptionException` that names the measure. With Copper, call `CopperPostProcessor#disableExecutionInQueryCube()`:

```java theme={"languages":{"custom":["/engine/python-sdk/6.2/languages/pycon.tmLanguage.json"]}}
// Computed on each data cube, from the two versions of the delay that it conceals
Copper.newPostProcessor("DelayPostProcessor")
    .withUnderlyingMeasures("delay.v1", "delay.v2")
    .disableExecutionInQueryCube()
    .withType(ILiteralType.DOUBLE)
    .as("delay")
    .publish(context);
```

Each data cube computes such a post-processor from its own data. The query cube does not merge the results of several data cubes, whatever aggregation function the post-processor declares: query it where a single data cube contributes, at or below the distributing levels. A query above them, where several data cubes contribute to the same location, fails rather than adds up results that may not be additive.

A Copper calculation is computed on the query cube, and its execution cannot be disabled there. It cannot use a concealed measure. A data cube that conceals `delay.v1` fails at startup when it defines the following calculation:

```java theme={"languages":{"custom":["/engine/python-sdk/6.2/languages/pycon.tmLanguage.json"]}}
Copper.sum("delayInDays").as("delay.v1").publish(context);
// Fails at startup when delay.v1 is concealed: the query cube computes this calculation
Copper.measure("delay.v1").multiply(Copper.constant(2D)).as("delay.doubled").publish(context);
```

Write such a calculation as a post-processor whose execution is disabled in the query cube instead.

A measure that uses `delay` as an underlying measure, or any other measure with no concealed underlying measure, has no such constraint. It is computed on the query cube like any other measure of a distributed application.

## Which limits apply?

* A native measure, such as `contributors.COUNT` or `update.TIMESTAMP`, cannot be concealed: naming one fails with a `DescriptionException`. Every query cube defines its own native measures and requests them from every data cube.
* Every data cube of an application must conceal the same measures. All data cubes of an application must use the same measure definitions, as described in [Scalability](https://docs.activeviam.com/engine/java-sdk/6.2/distributed/distributed_intro.md#scalability), and a concealed measure is not sent to the query cubes.
* Concealing a measure that the data cube does not define has no effect, apart from a warning in the logs. Check the logs for a misspelled measure name.
* A measure defined on a query cube cannot use a concealed measure: the query cube does not expose it.
* Atoti finds the measures that use a concealed measure through their underlying measures. A post-processor that reads a concealed measure without listing it among its underlying measures is not detected. Disable its execution in the query cube explicitly.
