--- title: "Working with Fabric Eventhouses (real-time data)" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Working with Fabric Eventhouses (real-time data)} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE) ``` An Eventhouse stores event, log, and time-series data in KQL databases. In this guide, *ingestion* means adding rows to a KQL table. Start with the discovered database's `$write_table()` method (`fabric_kql_write_table()`), which accepts an R data frame and manages staging, status checks, and cleanup for you. After the basic write, this guide shows how to monitor ingestion, use an existing storage file, scale up with Arrow, and export a large query result. The lower-level ingestion routes used by these workflows are currently in preview. ## Find a KQL database Start with a discovered KQL database. The result is a read-only `FabricKqlDatabase` R6 object. Its fields contain both the query and ingestion URIs, and its methods reuse that context and the discovery credential: ```{r, eval = FALSE} library(fabricQueryR) database <- fabric_kql_databases("Telemetry workspace")[[1]] database$ingestion_service_uri ``` ## Write a small data frame in one call Supply a destination table and an ordinary data frame or tibble. Use a new test table while learning: ```{r, eval = FALSE} written <- database$write_table( table = "Events", data = data.frame( id = 1:3, category = c("A", "B", "A"), amount = c(10.5, 20, 30.5) ), create_if_missing = TRUE, ingest_if_not_exists = "r-events-2026-08-14" ) written$status$state written$rows written$staging_retained ``` The function writes temporary Parquet data, uploads it to Fabric, queues the ingestion, and waits for a final status. With the default `cleanup = TRUE`, service-owned Storage sources may be deleted after download, before ingestion succeeds. OneLake staging is removed only after confirmed success. Set `cleanup = FALSE` to retain Storage sources for recovery; a failed Storage batch otherwise reports `staging_retained = NA` because source retention is unknown. `create_if_missing = TRUE` creates a basic table from the R object's columns when needed. For an existing table, the source names and types must match. Supply a predefined Parquet `mapping` or explicit `column_types` when inference is not appropriate. See `?fabric_kql_write_table` for supported type mappings and staging recovery options. ## Queue a tracked batch Use the lower-level `$ingest()` method (`fabric_kql_ingest()`) when the source file already exists in OneLake or supported blob storage. It does not upload a local file or serialize an R object, and the destination table must already exist. The `mapping` argument is optional. When it is omitted, Kusto derives an identity mapping from the existing table schema: ordered text formats such as CSV map source columns by position, while JSON, Parquet, Avro, ORC, and W3CLOGFILE map fields to case-sensitive table-column names. Use a validated named mapping when source order or names differ, or when ingestion-time transformations are required. Supply the complete source path and its format: ```{r, eval = FALSE} source <- paste0( "https://onelake.dfs.fabric.microsoft.com/", "/", "/Files/events/2026-08-14.csv;impersonate" ) ``` The signed-in identity must be able to read the file. Do not print or log a source string that contains credentials. Source IDs are generated when omitted and remain available on the returned handle. This example uses identity mapping unless `FABRIC_KQL_INGESTION_MAPPING` names a predefined CSV mapping: ```{r, eval = FALSE} mapping <- Sys.getenv("FABRIC_KQL_INGESTION_MAPPING", unset = "") ingestion <- database$ingest( table = "Events", sources = source, format = "csv", mapping = if (nzchar(mapping)) mapping else NULL, ignore_first_record = TRUE, tags = "source:daily-export", ingest_if_not_exists = "events-2026-08-14" ) ingestion$id ingestion$sources$source_id ``` Use a stable `ingest_if_not_exists` key when the same source file may be submitted again. Idempotency keys require one source per call; submit multiple files separately with a distinct stable key for each file. Queued ingestion is an advanced, at-least-once workflow: after an uncertain network result, inspect the tracked operation and target table before submitting the source again. See `?fabric_kql_ingest` for batching, source deletion, and storage-authentication options. ## Wait and inspect the outcome One status snapshot with `$ingestion_status()` (`fabric_kql_ingestion_status()`) is useful for a scheduler that persists operation IDs: ```{r, eval = FALSE} snapshot <- database$ingestion_status(ingestion) snapshot$state snapshot$counts ``` For an interactive or single-process batch, `$ingestion_wait()` calls `fabric_kql_ingestion_status(..., wait = TRUE)` with a client-side deadline: ```{r, eval = FALSE} result <- database$ingestion_wait( ingestion, timeout = 900, poll_interval = 2 ) result$state result$details ``` The deadline stops only the R waiter; it does not cancel ingestion in Fabric. By default, a failed or partially successful batch raises an R condition. To inspect every terminal state as data instead, call `$ingestion_wait()` (`fabric_kql_ingestion_status(..., wait = TRUE)`) with `error_on_failure = FALSE`: ```{r, eval = FALSE} result <- database$ingestion_wait( ingestion, error_on_failure = FALSE ) failed <- subset( result$details, status %in% c("Failed", "Canceled") ) failed[c("source_id", "error_code", "failure_status", "message")] ``` Query the destination after success with `$query()` (`fabric_kql_query()`): ```{r, eval = FALSE} loaded <- database$query( query = "Events | where ingestion_time() > ago(1h) | take 100" ) ``` The caller needs permission to ingest into the destination table and read the source file. Keep a manually staged source until the tracked result is final and verified. With `$write_table()` (`fabric_kql_write_table()`), select `cleanup = FALSE` if service-owned Storage sources must remain available until you verify the result; the default permits deletion after download. ## Scale up with Arrow For data larger than memory, pass an Arrow Dataset, Scanner, 'dplyr' query, RecordBatchReader, or compatible stream to the same high-level writer: ```{r, eval = FALSE} dataset <- arrow::open_dataset("local-parquet-directory") written <- database$write_table( table = "Events", data = dataset, mapping = "EventsParquet" ) ``` Here `$write_table()` calls `fabric_kql_write_table()`, as in the data-frame example above. The source is processed in batches instead of first being collected into an R data frame. A supplied RecordBatchReader is single-use. ## Export a large KQL result to OneLake `$query()` (`fabric_kql_query()`) is the right interface when the result belongs in R. When the result is too large for the client-result channel or should remain in Fabric, `$export()` (`fabric_kql_export()`) runs Kusto's service-side export and writes the first result set directly to storage: ```{r, eval = FALSE} lakehouse <- fabric_lakehouses("Telemetry workspace")[[1]] exported <- database$export( query = "Events | where amount > 0", destination = lakehouse, path = "Files/exports/events-positive-amount", format = "parquet", name_prefix = "events", compression_type = "snappy" ) exported$state exported$records exported$artifacts ``` The signed-in identity needs write access to the destination. If an export fails, treat any files already written as incomplete and inspect the returned operation before starting a replacement export.