Apache Spark processes data using compute that runs in Fabric. Livy is the service that lets an R program submit Spark code and receive its status and output. The Spark code runs on Fabric; it does not run in your local R process.
Use Spark when a transformation is too large for one computer, needs a Spark-specific library or format, or already exists as a Spark application. For a small table read, SQL or a direct Lakehouse reader is usually simpler and starts faster. This guide begins with one small statement, then moves to reusable sessions, complete applications, and shared environments.
Before the first call
You need a Fabric workspace on supported capacity, a Lakehouse with a Livy endpoint, and the tenant admin setting for the Livy API enabled.
A delegated caller needs all four of these Microsoft Entra scopes:
Lakehouse.Execute.AllLakehouse.Read.AllCode.AccessFabric.AllCode.AccessStorage.All
With delegated sign-in and audience = NULL,
‘fabricQueryR’ requests those four scopes. The following
Code.* scopes are optional; add one only when the Spark
code needs the corresponding Azure service at runtime:
| Optional scope | Runtime access |
|---|---|
Code.AccessAzureKeyvault.All |
Azure Key Vault secrets, keys, or certificates |
Code.AccessAzureDataLake.All |
Azure Data Lake Storage Gen1 |
Code.AccessAzureDataExplorer.All |
Azure Data Explorer (Kusto) |
Code.AccessSQL.All |
Azure SQL |
An explicit audience replaces the defaults rather than
extending them. For example, include all four required scopes when
adding Azure SQL access. The Lakehouse $livy_query() method
below calls fabric_livy_query():
livy_scopes <- paste0(
paste0("https", "://api.fabric.microsoft.com/"),
c(
"Lakehouse.Execute.All",
"Lakehouse.Read.All",
"Code.AccessFabric.All",
"Code.AccessStorage.All",
"Code.AccessSQL.All"
)
)
# `$livy_query()` is the object interface to `fabric_livy_query()`
result <- lakehouse$livy_query(
code = "SELECT * FROM external_sql_table",
kind = "sql",
audience = livy_scopes
)Client-credentials authentication instead uses the single
Fabric/Power BI .default audience selected by the package.
Microsoft currently documents service-principal (SPN) tokens for session
jobs. Add that principal to the workspace as a Contributor, and
confirm that the tenant’s Livy settings allow it; the workspace role
alone does not guarantee service-side acceptance.
Microsoft’s current batch guide is internally inconsistent: its introduction says Azure SPN is unsupported, while its authentication section gives a certificate-based SPN example. ‘fabricQueryR’ can acquire and send an app-only token, but it cannot override a service-side identity restriction. Verify unattended batch authentication in the target tenant and use a delegated user if Fabric rejects the SPN.
The delegated user must be a Contributor in the workspace containing the Livy endpoint and data-source items.
Then, you can discover the Lakehouse item which you want to use Livy with:
library(fabricQueryR)
workspaces <- fabric_workspaces()
matches <- Filter(
\(x) identical(x$displayName, "Analytics workspace"),
workspaces
)
stopifnot(length(matches) == 1L)
workspace <- matches[[1L]]
lakehouse <- workspace$lakehouses()[[1L]]$lakehouses() is the workspace method for
fabric_lakehouses().
The discovered FabricLakehouse is a read-only R6 object.
Its fields expose the service metadata. $livy_query(),
$livy_session(), and $livy_batch_submit()
correspond to fabric_livy_query(),
fabric_livy_session(), and
fabric_livy_batch_submit().
If discovery cannot retrieve the endpoint in your environment, copy the session-job connection string from Lakehouse settings > Livy endpoint and pass that URL instead.
Run one piece of Spark code
$livy_query() (fabric_livy_query()) is the
simplest method. It starts a temporary session, runs one statement,
waits, and closes the session:
result <- lakehouse$livy_query(
kind = "sql",
code = "SELECT 1 AS id, 'hello from Spark' AS message"
)
result$output$parsedThe returned output$parsed value is usually a tibble for
tabular results, an R object for JSON, or character output for printed
text.
Choose the language that matches the code
The kind argument tells Livy how to interpret
code:
Microsoft recommends the latest generally available runtime for
production, currently Fabric
Runtime 2.0 (Spark 4.1). SparkR still works, but SparkR is
deprecated upstream in Spark 4.x and may be removed in a future Spark
release. Microsoft Fabric distributes sparklyr
for R-first workloads in notebooks and Spark job definitions. Microsoft
does not currently document sparklyr over an item-scoped
Livy session. Choose PySpark or Spark SQL when the remote workload must
be independent of the current SparkR runtime bridge.
kind |
Code language |
|---|---|
"sql" |
Spark SQL |
"sparkr" |
R: SparkR; an experimental sparklyr bridge is described below |
"pyspark" |
Python with Spark |
"spark" |
Scala |
For example, SparkR code can use the active Spark session and Lakehouse:
This again uses $livy_query()
(fabric_livy_query()):
result <- lakehouse$livy_query(
kind = "sparkr",
code = paste(
"df <- sql('SELECT * FROM orders LIMIT 100')",
"printSchema(df)",
"showDF(df, numRows = 10)",
sep = "\n"
)
)sparklyr is not another Livy kind. The
following is an experimental adaptation of Fabric’s documented
notebook/Spark-job "synapse" connection; Microsoft does not
document it for item-scoped Livy, and ‘fabricQueryR’ does not currently
live-test it. If you evaluate that bridge in your tenant, sparklyr code
submitted through Livy still uses kind = "sparkr":
result <- lakehouse$livy_query(
kind = "sparkr",
code = paste(
"library(sparklyr)",
"spark_version <- sparkR.version()",
"config <- spark_config()",
paste0(
"sc <- spark_connect(master = 'yarn', version = spark_version, ",
"spark_home = '/opt/spark', method = 'synapse', config = config)"
),
"orders <- dplyr::tbl(sc, 'orders')",
"print(dplyr::collect(head(orders, 10)))",
"spark_disconnect(sc)",
sep = "\n"
)
)Even when it works, this removes only the application-level dependency on SparkR’s DataFrame verbs. It remains dependent on the R interpreter and SparkR JVM bridge, so validate it after every runtime upgrade and do not treat this example as a supported production contract.
Reuse a session for several statements
Starting Spark can take time. Use $livy_session()
(fabric_livy_session()) when sequential statements need to
share variables or cached data:
answer <- local({
session <- lakehouse$livy_session()
on.exit(session$close(), add = TRUE)
session$wait()
session$run("shared_value = 40", kind = "pyspark")
answer <- session$run("print(shared_value + 2)", kind = "pyspark")
answer
})
answer$output$parsedAlways close a session explicitly. R object cleanup does not make a
network request, so forgetting $close() can leave Spark
running. The returned FabricLivySession lifecycle methods
($wait(), $run(), and $close())
do not have separate free-function wrappers.
A standard session is right for one R process running a sequence. High concurrency is an advanced option for several isolated workloads that may share underlying compute; it is not needed for a few statements in order.
Submit a complete application file
Use $livy_batch_submit()
(fabric_livy_batch_submit()) when the work is a repeatable
Python, R, or Java/Scala script stored in OneLake or ADLS:
batch <- lakehouse$livy_batch_submit(
file = paste0(
"abfss://", workspace$id,
"@onelake.dfs.fabric.microsoft.com/",
lakehouse$id,
"/Files/jobs/daily_transform.py"
),
name = "daily-transform",
wait = TRUE,
timeout = 1800
)
batch$result()The application file must already be available through an ABFS or
ABFSS path. Percent-encode spaces and other URL-reserved characters in
file path segments; raw spaces and whitespace in the workspace or
filesystem authority are invalid. Upload the file with
$onelake_upload() (fabric_onelake_upload())
first when necessary. With wait = FALSE, the function
returns a FabricLivyBatch object immediately; call its
$wait(), $result(), or $logs()
methods later.
The FabricLivyBatch lifecycle methods
($wait(), $result(), $logs(),
$status(), and $cancel()) do not have separate
free-function wrappers.
If a session, statement, or batch wait times out, the
fabric_livy_timeout_error condition keeps the exact live
object in its handle field. You can inspect
$status() or request $cancel() through that
handle in the current R process. The kind-specific session,
statement, or batch field contains safe
metadata for logging; after serialization, a handle intentionally no
longer carries its credential.
Use an Environment for repeatable configuration
A published Fabric Environment can hold Spark settings and libraries shared by several runs. Discover it and pass its ID when the workload depends on that configuration:
environment <- workspace$environments()[[1L]]
result <- lakehouse$livy_query(
kind = "pyspark",
code = "print(spark.version)",
environment_id = environment$id
)$environments() is the workspace method for
fabric_environments(). The Lakehouse
$livy_query() method calls
fabric_livy_query().