Skip to content
Snippets Groups Projects
Commit 75c63b1c authored by Sergey Yakubov's avatar Sergey Yakubov
Browse files

Merge pull request #192 in ASAPO/asapo from docs to develop

* commit '0bdea405':
  update docs for 22.03.0
parents a7514198 0bdea405
No related branches found
No related tags found
No related merge requests found
Showing
with 681 additions and 12 deletions
## 22.03.0 (in progress)
## 22.03.0
FEATURES
* Monitoring: Added detailed monitoring and pipeline visualization
* Consumer API: return kDataNotInCache/AsapoDataNotInCacheError error if data is not in cache and cannot be on disk (due to the ingest mode producer used)
IMPROVEMENTS
* renamed and hid C++ macros from client code
* Consumer API: return kDataNotInCache/AsapoDataNotInCacheError error if data is not in cache and cannot be on disk (due to the ingest mode producer used)
BUG FIXES
* Producer API: fixed bug segfault in Python code when sending data object which memory is from some other object
VERSION COMPATIBILITY
* Previous C consumer & producer clients will break due to two extra parameters for instance id and pipeline step id in *asapo_create_source_credentials*.
INTERNAL
* Do not return error when receiver cannot get slot in shared cache - just allocate own memory slot
......
---
title: Version 22.03.0
author: Sergey Yakubov
author_title: DESY IT
tags: [release]
---
#Changelog for version 22.03.0
FEATURES
* Monitoring: Added detailed monitoring and pipeline visualization
* Consumer API: return kDataNotInCache/AsapoDataNotInCacheError error if data is not in cache and cannot be on disk (due to the ingest mode producer used)
IMPROVEMENTS
* renamed and hid C++ macros from client code
BUG FIXES
* Producer API: fixed bug segfault in Python code when sending data object which memory is from some other object
VERSION COMPATIBILITY
* Previous C consumer & producer clients will break due to two extra parameters for instance id and pipeline step id in *asapo_create_source_credentials*.
INTERNAL
* Do not return error when receiver cannot get slot in shared cache - just allocate own memory slot
......@@ -10,6 +10,7 @@ Consumer API is available for C++ and Python and has the following main function
- Create a consumer instance and bind it to a specific beamtime and data source
- multiple instances can be created (also within a single application) to receive data from different sources
- an instance id and pipeline step id can be set to allow pipeline monitoring
- a beamtime token is used for access control
- If needed (mainly for get_next_XX operations), create a consumer group that allows to process messages independently from other groups
- Receive messages from a specific stream (you can read more [here](data-in-asapo) about data in ASAPO)
......
......@@ -47,7 +47,7 @@ unix socket or a tcp port for communications)
at the end you should see
<p className="green-text"><strong>Apply complete! Resources: 19 added, 0 changed, 0 destroyed.</strong></p>
<p className="green-text"><strong>Apply complete! Resources: 25 added, 0 changed, 0 destroyed.</strong></p>
which means ASAPO services successfully started. Your ASAPO endpoint for API calls will be **localhost:8400**.
......
......@@ -9,7 +9,7 @@ Producer client (or producer) is a part of a distributed streaming system that i
Producer API is available for C++ and Python and has the following main functionality:
- Create a producer instance and bind it to a specific beamtime and data source
multiple instances can be created (also within a single application) to send data from different sources
multiple instances can be created (also within a single application) to send data from different sources. An instance id and pipeline step id can be set to allow pipeline monitoring.
- Send messages to a specific stream (you can read more [here](data-in-asapo) about data in ASAPO)
- each message must have a consecutive integer index, ASAPO does not create indexes automatically
- to compose datasets, dataset substream (and dataset size) should be send along with each message
......
......@@ -26,7 +26,7 @@ int main(int argc, char* argv[]) {
const char * path_to_files = "/var/tmp/asapo/global_shared/data/test_facility/gpfs/test/2019/data/asapo_test"; //set it according to your configuration.
AsapoSourceCredentialsHandle cred = asapo_create_source_credentials(kProcessed,
"auto", "auto", beamtime,
"test_consumer_instance", "pipeline_step_1", beamtime,
"", "test_source", token);
AsapoConsumerHandle consumer = asapo_create_consumer(endpoint,
path_to_files, 1,
......
......@@ -29,11 +29,13 @@ int main(int argc, char* argv[]) {
auto credentials = asapo::SourceCredentials
{
"test_consumer_instance", // # conumser instance id (can be "auto")
"pipeline_step_1", // # pipeline step id
asapo::SourceType::kProcessed, // should be kProcessed or kRaw, kProcessed writes to the core FS
beamtime, // the folder should exist
"", // can be empty or "auto", if beamtime_id is given
"test_source", // source
token // athorization token
token // authorization token
};
auto consumer = asapo::ConsumerFactory::CreateConsumer
......
......@@ -34,11 +34,13 @@ int main(int argc, char* argv[]) {
auto credentials = asapo::SourceCredentials
{
"test_producer_instance", // # producer instance id (can be "auto")
"pipeline_step_1", // # pipeline step id
asapo::SourceType::kProcessed, // should be kProcessed or kRaw, kProcessed writes to the core FS
beamtime, // the folder should exist
"", // can be empty or "auto", if beamtime_id is given
"test_source", // source
"" // athorization token
"" // authorization token
};
auto producer = asapo::Producer::Create(endpoint,
......
......@@ -22,7 +22,10 @@ consumer = asapo_consumer \
beamtime, # Same as for the producer
"test_source", # Same as for the producer
token, # Access token
5000) # Timeout. How long do you want to wait on non-finished stream for a message.
5000, # Timeout. How long do you want to wait on non-finished stream for a message.
'test_consumer_instance', # conumser instance id (can be 'auto')
'pipeline_step_1' # pipeline step id
)
#create snippet_end
#list snippet_start
......
......@@ -23,9 +23,12 @@ producer = asapo_producer \
beamtime, # the folder should exist
'auto', # can be 'auto', if beamtime_id is given
'test_source', # source
'', # athorization token
'', # authorization token
1, # number of threads. Increase, if the sending speed seems slow
60000) # timeout. Do not change.
60000, # timeout. Do not change.
'test_producer_instance', # producer instance id (can be 'auto')
'pipeline_step_1' # pipeline step id
)
producer.set_log_level("error") # other values are "warning", "info" or "debug".
# create snippet_end
......
......@@ -35,4 +35,4 @@ docker run --privileged --rm -v /var/run/docker.sock:/var/run/docker.sock \
--name asapo --net=host -d yakser/asapo-cluster-dev:100.0.develop
sleep 15
docker exec asapo jobs-start -var elk_logs=false -var influxdb_version=1.8.4
docker exec asapo jobs-start
......@@ -44,4 +44,4 @@ docker run --privileged --userns=host --security-opt no-new-privileges --rm \
--name asapo --net=host -d yakser/asapo-cluster-dev:100.0.develop
sleep 15
docker exec asapo jobs-start -var elk_logs=false
docker exec asapo jobs-start
---
title: Comparison to Other Solutions
---
Here we consider how ASAPO is different from other workflows practiced at DESY. The possible candidates are:
### Filesystem
Probably the most often used approach for now. Files are written to the beamline filesystem directly via NFS/SMB mount or by HiDRA and copied to the core filesystem by a copy daemon. A user (software) then reads the files from the filesystem.
### Filesystem + Kafka
Previous workflow + there is a Kafka instance that produces messages when a file appears in the core filesystem. These messages can then be consumed by user software.
### HiDRA
HiDRA can work in two modes - wether data is transferred via it or data is written over NFS/SMB mounts and HiDRA monitors a folder in a beamline filesystem. In both case one can subscribe to HiDRA's data queue to be informed about a new file.
### ASAPO
ASAPO does not work with files, rather with data streams. Well, behind the scene it does use files, but in principle what a user see is a stream of messages, where a message typically consists of metadata and data blobs. Data blob can be a single image, a file with arbitrary content or whatever else, even null. Important is - what goes to one end, appears on the other. And that each message must have a consecutive index. These messages must be ingested to an ASAPO data stream in some way (e.g. using ASAPO Producer API or HiDRA) and data, if not yet, will be transferred and stored in the data center. A user can then read the messages from the stream and process it in a way he likes.
### Compare by categories
In the table below we compare the approaches from different points of view and in the [next table](#compare-by-features) we compare the approaches by available features.
| Category | Filesystem | Filesystem+Kafka | HiDRA | ASAPO |
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Data ingest | traditional way - write to disk | same as Filesystem, additionally a message is send to a Kafka topic with basic file metadata (name, size, creation date, ...) | outperforms data ingest via NFS/SMB, uses ZeroMQ(TCP), saturates 10GE bandwidth. To be tested with 100GE. Same metadata as with Kafka | uses parallel TCP connections & in the future RDMA. Can add arbitrary metadata (JSON format). Can send data to various streams to create arbitrary data hierarchy (e.g. stream per scan). Saves data to a memory buffer and on disk. |
| Offline analysis | traditional way - read from disk. Need to know the filename to read, usually not a problem. | same as Filesystem, there is no benefits reading Kafka queue after all data is arrived | not possible, need to fallback to Filesystem | same as online analysis (see below) |
| Online analysis | no efficient way to recognise that new data is available - periodically read folder content and compare with previous state?, periodically check file appeared? | using a subscription to a "new files" topic, a user software can be made aware of new data quite soon and react correspondingly. | one can subscribe to a HiDRA's ZeroMQ stream and consume data. If data arrives faster than it can be processed or e.g. user software crashes - data might be skipped. | one can get data from various streams in different ways - get next unprocessed message ordered by index, get last, get by id. Since everything is stored in persistent storage, processing is possible with arbitrary slow (but also fast) consumers. Resilient to connections loss or consumer crashes. |
| Performance | as good as read/write to disk. Can be an issue, especially with new detectors 100GE+ networks. | as good as read/write to disk + some latency for the file to be written to the beamline filesystem, copied to the core filesystem and a message to go through Kafka. | data is available as soon as it is received by HiDRA. If multiple consumer groups want to read same data (one consumer is known - the process that writes the file), data will be transferred multiple times, which influences the network performance. | data is available to be consumed as soon as it is arrived and saved to beamline filesystem (later can be optimised by using persistent memory instead). No need to read from disk since it also remains in memory buffer. |
| Parallelisation | Parallelisation is easily possible e.g. with an MPI library. | Parallelisation is possible if Kafka's topics are partitioned (which is not the case in the moment) | Not out of the box, possible with some changes from user's side | Parallelisation is easily possible, one can consume data concurrently with different consumers from the same stream. Normally, synchronisation between consumers is not needed, but this might depend on a use case. When configured, data can be resent if not acknowledged during a specified time period. |
| Search/filter | hardly possible, manually parsing some metadata file, using POSIX commands? | same as Filesystem. There is Kafka SQL query language which could be used if there would be metadata in messages, which is not the case (yet?). | not possible | one can use a set of SQL queries to ingested metadata. |
| General comments | Might be ok for slow detectors or/and a use case without online analysis requirements. Might be the only way to work with legacy applications | Fits well for the cases where a software just need a trigger that some new data has arrived, when processing order, extra metadata, parallelisation is not that important or implemented by other means. Some delay between an image is generated and the event is emitted by Kafka is there, but probably not that significant (maximum a couple of seconds). Might be not that appropriate for very fast detectors since still using filesystem to write/read data. | Works quite well to transfer files from detector to the data center. Also a good candidate for live viewers, where the last available "image" should be displayed. Does not work for offline analysis or for near real-time analysis where image processing can take longer than image taking. | Tries to be a general solution which improves in areas where other approaches not suffice: single code for offline/near real-time/online analysis, parallelisation, extended metadata, efficient memory/storage management, getting data without access to filesystem (e.g. from detector pc without core filesystem mounted), computational pipelines, ... Everything has its own price: user software must be modified to use ASAPO, a wrapper might be needed for legacy software that cannot be modified, user/beamtime scientist should better structure the data - e.g. consecutive indexes must be available for each image, one has to define to which stream write/read data, what is the format of the data, ... |
### Compare by features
| Feature | Filesystem | Filesystem+Kafka | HiDRA | ASAPO |
|--------------------------------------------------------------------------------------------------------------------|------------------------------------|------------------------------------|------------------------------------|------------------------------------|
| send metadata with image | No | No | No | Yes |
| get last image | No | No | Yes | Yes |
| get image by id | No | No | No | Yes |
| get image in order | No | No | No | Yes |
| Immediately get informed that a new image is arrived | No | Yes | Yes | Yes |
| access image remotely, without reading filesystem | No | No | Yes, if it is still in buffer | Yes |
| access past images | Yes | Yes | No | Yes |
| need to change user code | No | Yes | Yes | Yes |
| parallelisation | Yes (if user software allows that) | Not out of the box | Not out of the box | Yes |
| legacy applications | Yes | No (wrapper could be a workaround) | No (wrapper could be a workaround) | No (wrapper could be a workaround) |
| transparent restart/continuation of simulations in case e.g. worker process crashes, also for parallel simulations | Not out of the box | Yes | No | Yes |
---
title: Consumer Clients
---
Consumer client (or consumer) is a part of a distributed streaming system that is responsible for processing streams of data that were created by producer. It is usually a user (beamline scientist, detector developer, physicist, ... ) responsibility to develop a client for specific beamline, detector or experiment using ASAPO Consumer API and ASAPO responsibility to make sure data is delivered to consumers in an efficient and reliable way.
![Docusaurus](/img/consumer-clients.png)
Consumer API is available for C++ and Python and has the following main functionality:
- Create a consumer instance and bind it to a specific beamtime and data source
- multiple instances can be created (also within a single application) to receive data from different sources
- an instance id and pipeline step id can be set to allow pipeline monitoring
- a beamtime token is used for access control
- If needed (mainly for get_next_XX operations), create a consumer group that allows to process messages independently from other groups
- Receive messages from a specific stream (you can read more [here](data-in-asapo) about data in ASAPO)
- GetNext to receive process messages one after another without need to know message indexes
- Consumer API returns a message with index 1, then 2, ... as they were set by producer.
- This also works in parallel so that payload is distributed within multiple consumers within same consumer group or between threads of a single consumer instance. In parallel case order of indexes of the messages is not determined.
- GetLast to receive last available message - for e.g. live visualisation
- GetById - get message by index - provides random access
- Make queries based on metadata contained in a message - returns all messages in a stream with specific metadata. A subset of SQL language is used
All of the above functions can return only metadata part of the message, so that an application can e.g. extract the filename and pass it to a 3rd party tool for processing. Alternative, a function may return the complete message with metadata and data so that consumer can directly process it. An access to the filesystem where data is actually stored is not required in this case.
:::note
In case of dataset family of functions, only list of dataset messages is returned, the data can be retrieved in a separate call.
:::
Please refer to [C++](http://asapo.desy.de/cpp/) and [Python](http://asapo.desy.de/python/) documentation for specific details (available from DESY intranet only).
---
title: Acknowledgements
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
While consuming the messages we could issue acknowledgements, to denote that the messages were (or were not) processed successfully.
Here is the snippet that expects 10 sample messages in the default stream. When consuming the messages, the message #3 receives a negative acknowledgement, which puts is back in the stream for the repeated processing, and the messages 5 and 7 remain unacknowledged. On the second attempt the message #3 gets acknowledged.
You can found the full example in git repository.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/acknowledgements.py" snippetTag="consume"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/acknowledgements.cpp" snippetTag="consume"
```
</TabItem>
</Tabs>
The list of unacknowledged messages can be accessed at any time. This snippet prints the list of unacknowledged messages.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/acknowledgements.py" snippetTag="print"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/acknowledgements.cpp" snippetTag="print"
```
</TabItem>
</Tabs>
The output will show the order in which the messages receive their acknowledgements. You may notice that the second acknowledgement of the message #3 happens with a delay, which was deliberatly chosen. The unacknowledged messages are retrieved separately at the end, after the consumer timeout.
---
title: Datasets
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
The messages in the stream can be multi-parted. If you have several producers (e.g. sub-detectors) that produces several parts of the single message, you can use datasets to assemble a single message from several parts.
## Dataset Producer
Here is the code snippet that can be used to produce a three-parted dataset. The full usable example can be found in git repository.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/produce_dataset.py" snippetTag="dataset"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/produce_dataset.cpp" snippetTag="dataset"
```
</TabItem>
</Tabs>
You should see the "successfuly sent" message in the logs, and the file should appear in the corresponding folder (by default in ```/var/tmp/asapo/global_shared/data/test_facility/gpfs/test/2019/data/asapo_test```)
## Dataset Consumer
Here is the snippet that can be used to consume a dataset. The full example is also in git.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/consume_dataset.py" snippetTag="dataset"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/consume_dataset.cpp" snippetTag="dataset"
```
</TabItem>
</Tabs>
The details about the received dataset should appear in the logs, together with the message "stream finished" (if the "finished" flag was sent for the stream). The "stream ended" message will appear for non-finished streams, but may also mean that the stream does not exist (or was deleted).
---
title: Metadata
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
You can also store any custom metadata with your beamtime, stream, and each message. This tutorial shows you how you can store, update and access this metadata. The metadata is stored in JSON, and any JSON structure is supported.
:::info
Since C++ doesn't have a built-in JSON support, you'd have to use 3rd party libs if you want JSON parsing. In this tutorial we won't use any JSON parsing for C++, and will treat JSONs as regular strings. Please note, that ASAP::O only supports valid JSONs, and providing invalid input will result in error.
:::
## Send Metadata
The following snippet shows how to send the beamtime metadata.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="beamtime_set"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="beamtime_set"
```
</TabItem>
</Tabs>
Each metadata can be updated at any moment. Here is the example on how to do it with beamtime metadata.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="beamtime_update"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="beamtime_update"
```
</TabItem>
</Tabs>
The same way the metadata can be set for each stream.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="stream_set"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="stream_set"
```
</TabItem>
</Tabs>
And for each message
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="message_set"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="message_set"
```
</TabItem>
</Tabs>
## Read Metadata
Here we will read the beamtime metadata. In this example it will already incorporate the changes we did during the update
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="beamtime_get"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="beamtime_get"
```
</TabItem>
</Tabs>
Same for the stream.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="stream_get"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="stream_get"
```
</TabItem>
</Tabs>
And for the message.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/metadata.py" snippetTag="message_get"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/metadata.cpp" snippetTag="message_get"
```
</TabItem>
</Tabs>
The output will show the metadata retrieved from the beamtime, stream and message.
---
title: Stream Finishing
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
When all the data in the stream is sent, the stream can be finished, and it is posiible to set the "next stream" to follow up the first. In this tutorial it'll be shown how several streams can be chained together in single consumer by using the stream finishing.
The setting of the next stream is done by providing an additional parameter while finishing the stream
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/next_stream.py" snippetTag="next_stream_set"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/next_stream.cpp" snippetTag="next_stream_set"
```
</TabItem>
</Tabs>
The reading of the streams can be then chained together. When one stream finishes, and the next stream is provided, the reading of the next stream can immediately start. This example will read the whole chain of streams, until it encounters the non-finished stream, or the stream that was finished without the ```next```.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/next_stream.py" snippetTag="read_stream"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/next_stream.cpp" snippetTag="read_stream"
```
</TabItem>
</Tabs>
The output will show the messages being consumed from the streams in order. For this example (full file can be found in git repository) it'll be first the ```default``` stream, then the ```next```.
---
title: Code Examples Overview
---
Here you can find the code examples for various common asapo usecases. Make sure that the ASAP::O instance and client libraries are properly installed, see [Getting Started page](../) for details.
For the most basic usecase, see the [Simple Producer](simple-producer) and [Simple Consumer](simple-consumer). There are also the basic examples of CMake and makefile configurations for client compilation.
The API documentation can be found [here](http://asapo.desy.de/cpp) (for C++) or [here](http://asapo.desy.de/python) (for python).
:::tip
You can see more examples in ASAPO [source code](https://stash.desy.de/projects/ASAPO/repos/asapo/browse/examples)
:::
---
title: Message query
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Messages in streams can be retrieved based on their metadata. Both the technical information (e.g. ID or timestamp) and the user metadata (see [this tutorial](metadata) for details) can be used to make a query. In this tutorial several examples of the queries are shown. The standard SQL sysntaxis is used.
For this example we expect several messages in the default stream with the metadata consisting of two fields: a string named ```condition``` and an integer named ```somevalue```. Go to the git repository for the full example.
:::info
Keep in mind, that the query requests return only the list of metadatas for the found messages, not the messages itself. You need to explicitly retrieve the actual data for each message.
:::
Here we can pick a message with the specific ID.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/query.py" snippetTag="by_id"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/query.cpp" snippetTag="by_id"
```
</TabItem>
</Tabs>
We can also use the simple rule for picking a range of IDs
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/query.py" snippetTag="by_ids"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/query.cpp" snippetTag="by_ids"
```
</TabItem>
</Tabs>
We can query the messages based on their metadata, for example request a specific value of the string field.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/query.py" snippetTag="string_equal"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/query.cpp" snippetTag="string_equal"
```
</TabItem>
</Tabs>
We can also require some more complex constraints on the metadata, e.g. a range for an integer field
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/query.py" snippetTag="int_compare"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/query.cpp" snippetTag="int_compare"
```
</TabItem>
</Tabs>
Since every message comes with a timestamp, we can make constraints on it as well. For example, request all the messages from the last 15 minutes.
<Tabs
groupId="language"
defaultValue="python"
values={[
{ label: 'Python', value: 'python', },
{ label: 'C++', value: 'cpp', },
]
}>
<TabItem value="python">
```python content="./versioned_examples/version-22.03.0/python/query.py" snippetTag="timestamp"
```
</TabItem>
<TabItem value="cpp">
```cpp content="./versioned_examples/version-22.03.0/cpp/query.cpp" snippetTag="timestamp"
```
</TabItem>
</Tabs>
The output of the full example will show the message selection together with the conditions used for selection.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment