Operate RelationalAI
Users can run PyRel programs that depend on the RAI Native App for Snowflake only while the app service is active and ready. Use this guide to operate the app after installation: start it, confirm that it’s healthy, monitor it with observability metrics and logs, troubleshoot activation problems, and stop it when you no longer need it running.
- Prerequisites
- The RAI Native App is already installed in your Snowflake account.
- You can run SQL in Snowflake with an active warehouse.
- To start or stop the app, you have the
app_adminapplication role. - To enable app observability, you have the
observability_adminapplication role.
How the app runs
Section titled “How the app runs”The app runs as a service in your Snowflake account that you start, monitor, and stop.
When you start the app:
- The RAI Native App service in Snowflake is turned on so users can run PyRel programs.
- The app’s compute pools are provisioned and reasoners are made available to evaluate queries from semantic models.
Activation and the CDC service are separate:
- On first activation, CDC stays disabled. Enable it explicitly with
app.resume_cdc(). See Enable change data capture in the setup guide. - On reactivation of a previously deactivated app, CDC returns to its earlier state (enabled or suspended); you do not need to resume it again.
When you stop the app, it suspends the app’s CDC service, deletes all reasoners, and cancels in-progress jobs. Stopping the app does not uninstall it from your Snowflake account, so you can start it again without reinstalling. No data is lost when you stop the app.
Start the app
Section titled “Start the app”Requires the app_admin application role.
Select the tab for your preferred language to see how to start the RAI Native App.
Use the relationalai.app.activate() procedure:
CALL relationalai.app.activate();+----------------------------------------------+| RelationalAI service activated successfully. || The service may still be starting up; check. || that the service is running by calling the. || APP.SERVICE_STATUS() procedure to check for || the READY status. |+----------------------------------------------+Create a Snowpark session from your default PyRel connection with create_config(), then call the relationalai.app.activate() SQL procedure:
from relationalai.config import create_config
session = create_config().get_session()
# Start the app.session.sql("CALL RELATIONALAI.APP.ACTIVATE()").collect()If activation returns an error, see Fix app activation errors caused by Snowflake account settings.
Check the app’s health
Section titled “Check the app’s health”Use the app’s status procedures to confirm whether the RAI Native App is ready to use, stopped, or still starting.
Check that the RAI service is running
Section titled “Check that the RAI service is running”Requires the app_user application role.
Use app.service_status() to check whether the RAI service containers are running and ready:
Call the app.service_status() procedure:
CALL relationalai.app.service_status();+----------------------------------------------+| [ || { || "message": "Running", || "name": "main", || "restartCount": 0, || "startTime": "2024-10-27T22:10:05Z", || "status": "READY" || }, || { || "message": "Running", || "name": "otel-collector", || "restartCount": 0, || "startTime": "2024-10-27T22:10:07Z", || "status": "READY" || }, || { || "message": "Running", || "name": "registry", || "restartCount": 0, || "startTime": "2024-10-27T22:10:06Z", || "status": "READY" || } || ] |+----------------------------------------------+Create a Snowpark session from your default PyRel connection with create_config(), then call the app.service_status() SQL procedure:
from relationalai.config import create_config
session = create_config().get_session()
status = session.sql("CALL relationalai.app.service_status()").collect()print(status)- If each container’s status is
READY, the service is activated and ready to use. - If one or more containers report a status such as
SUSPENDEDorSUSPENDING, the service is stopped and not ready to use. - If the call to
app.service_status()fails, the service may not exist, which can occur after a deeper shutdown that drops the service instead of just suspending it.
Check if the RAI service is stopped
Section titled “Check if the RAI service is stopped”Requires the app_user application role.
You can stop the RAI Native App in order to reduce costs during periods of inactivity. When the app is stopped, the app’s SPCS service can either be suspended or fully dropped, depending on the shutdown method used and how long ago the shutdown occurred.
Follow these steps to check whether the service has been suspended or dropped:
-
Check whether the service still exists
Run
app.get_service():CALL relationalai.app.get_service();This step tells you whether Snowflake still has a managed app service to inspect.
- If
app.get_service()returns service metadata, the service still exists. - If
app.get_service()shows no service metadata, the service was removed during a deeper shutdown and must be created again before it can start.
- If
-
Check whether the existing service is stopped or running
If the service still exists, run
app.service_status():CALL relationalai.app.service_status();This step tells you whether the existing service is stopped, still starting, or ready to use.
If the service still exists but is stopped,
app.service_status()can return output like this:Example +----------------------------------------------+| [ || { || "message": "Suspended", || "name": "main", || "restartCount": 0, || "startTime": "2024-10-27T22:10:05Z", || "status": "SUSPENDED" || }, || { || "message": "Suspended", || "name": "otel-collector", || "restartCount": 0, || "startTime": "2024-10-27T22:10:07Z", || "status": "SUSPENDED" || }, || { || "message": "Suspended", || "name": "registry", || "restartCount": 0, || "startTime": "2024-10-27T22:10:06Z", || "status": "SUSPENDED" || } || ] |+----------------------------------------------+- If
app.service_status()returns container entries with a status such asSUSPENDEDorSUSPENDING, the service exists but is stopped and not ready to use. - If each container reports
READY, the service is running rather than stopped. - If the service was dropped during a deeper shutdown,
app.service_status()may fail because there is no service to inspect.
- If
Check whether startup is still in progress
Section titled “Check whether startup is still in progress”Requires the app_user application role.
Use app.service_status() when the app was recently started and you want to see whether Snowflake is still bringing the service online:
Call the app.service_status() procedure:
CALL relationalai.app.service_status();+-----------------------------------+| [ || { || "message": null, || "name": null, || "restartCount": null, || "startTime": null, || "status": "PENDING" || } || ] |+-----------------------------------+Create a Snowpark session from your default PyRel connection with create_config(), then call the app.service_status() SQL procedure:
from relationalai.config import create_config
session = create_config().get_session()
status = session.sql("CALL relationalai.app.service_status()").collect()print(status)- Look for
status=PENDINGto confirm that startup is still in progress. - While the service is still
PENDING, Snowflake can returnNULLforname,message,restartCount, andstartTimebecause the service containers are not ready yet. - Keep calling
app.service_status()until the returned status changes toREADY.
Monitor the app
Section titled “Monitor the app”Monitor the app with observability metrics from Snowflake’s Event Table, and use the app’s logs when you need more detail.
Enable app observability
Section titled “Enable app observability”Requires the observability_admin application role and SELECT access on the active Snowflake Event Table.
Enable observability by registering a secure view over Snowflake’s Event Table.
Once registered, the app reads metrics through that view and makes them available in the relationalai.observability_preview schema, such as reasoner memory, CPU, and workload demand.
How app observability works
Section titled “How app observability works”The Native App uses Snowflake’s Event Table to report internal metrics about app and reasoner usage, including memory, CPU, and demand.
To access those metrics, you create a secure view that filters the Event Table for relevant app-shared metric rows, then register that view with the app so it can read the metrics and make them available in the relationalai.observability_preview schema.
Registering this view also enables a pipeline that shares telemetry generated by deployed models with RelationalAI as the app provider.
Register a secure events view with the RAI Native App
Section titled “Register a secure events view with the RAI Native App”-
Find the active Event Table
Use the account setting first so the rest of the SQL points at the right table.
SHOW PARAMETERS LIKE 'EVENT_TABLE' IN ACCOUNT;SHOW TABLES LIKE 'EVENTS' IN SCHEMA SNOWFLAKE.TELEMETRY; -
Enable change tracking if needed
Registration fails if change tracking is disabled on the Event Table.
ALTER TABLE SNOWFLAKE.TELEMETRY.EVENTSSET CHANGE_TRACKING = TRUE; -
Create the filtered secure view
Create the view in a database and schema that you own. The filter keeps the view limited to RAI database rows and rows explicitly flagged for sharing with RAI.
CREATE DATABASE IF NOT EXISTS MONITORING;-- Change the schema name if needed.CREATE SCHEMA IF NOT EXISTS MONITORING.RAI_OBSERVABILITY;CREATE OR REPLACE SECURE VIEW MONITORING.RAI_OBSERVABILITY.RAI_OBSCOMMENT = 'RelationalAI Native App observability filtered events view'CHANGE_TRACKING = TRUEASSELECTTIMESTAMP,START_TIMESTAMP,RECORD_TYPE,RECORD_ATTRIBUTES,RECORD,TRACE,VALUE,RESOURCE_ATTRIBUTES['snow.executable.name'] as EXECUTABLE_NAMEFROM SNOWFLAKE.TELEMETRY.EVENTSWHERE (resource_attributes['snow.database.name']::STRING = 'RELATIONALAI'OR record_attributes['rai_share']::BOOLEAN = TRUE); -
Register the view with the app
Registration stores a persistent reference that the app uses to read the filtered telemetry.
CALL RELATIONALAI.app.ENABLE_OBSERVABILITY(SYSTEM$REFERENCE('view','MONITORING.RAI_OBSERVABILITY.RAI_OBS','PERSISTENT','SELECT')); -
Validate the registration
Do not continue until the status check is healthy.
CALL RELATIONALAI.app.CHECK_EVENTS_VIEW_STATUS();Events view activemeans the configuration is valid and events are flowing.No events view registeredmeans setup is incomplete.ERRORmeans the registration exists but the app cannot use it yet.
Where logs are written
Section titled “Where logs are written”The RAI Native App writes information to different Snowflake surfaces:
| Location | What it is used for |
|---|---|
| Active Snowflake Event Table | Continuous app telemetry and observability metrics. This is the main source for routine monitoring. |
RELATIONALAI.LOGGING.LOGS | Internal application log records. This table gives you direct access to recent log entries, but it is not intended for end-user consumption and should be treated as potentially sensitive operational data. |
RELATIONALAI.APP_STATE.CLIENT_LOG_STAGE | Filtered support-share files used when RelationalAI asks for deeper diagnostics. See Share logs with RelationalAI support for details. |
You do not need to query these surfaces directly for routine monitoring or troubleshooting. Refer to the following resources for observability and monitoring guidance.
What information is collected in logs
Section titled “What information is collected in logs”The Native App collects different kinds of operational data for different purposes: Event Table telemetry gives you high-level health and usage signals for monitoring, LOGGING.LOGS contains raw internal log messages and metadata for deeper diagnosis, and the support-share workflow produces a filtered subset of engine and optional control-plane logs for a specific incident or time window.
Troubleshoot the app
Section titled “Troubleshoot the app”Use this section when the app doesn’t start or when RelationalAI support asks for deeper diagnostics.
Fix app activation errors caused by Snowflake account settings
Section titled “Fix app activation errors caused by Snowflake account settings”If CALL relationalai.app.activate(); returns an error instead of starting the app, a Snowflake account setting may be blocking it.
The most common cause is the account parameter PREVENT_UNLOAD_TO_INTERNAL_STAGES.
When this parameter is enabled, activation fails with an error saying that PREVENT_UNLOAD_TO_INTERNAL_STAGES is stopping the RAI Native App from writing to its internal stage.
An internal stage is a temporary storage area inside Snowflake.
When PREVENT_UNLOAD_TO_INTERNAL_STAGES is TRUE, the app can’t write to its own internal stage.
This blocks the CDC service and data streams that load your data, so activation stops instead of starting the app in a broken state.
To fix the error, complete one of the options below, then run CALL relationalai.app.activate(); again.
| What to do | When to do it |
|---|---|
| Enable internal stage unloads for the RAI app only | You want to keep PREVENT_UNLOAD_TO_INTERNAL_STAGES enabled for the rest of your account. |
| Disable the setting account-wide | You can disable the parameter for the whole account. |
After activation succeeds, confirm the app is ready by following Check that the RAI service is running.
Enable internal stage unloads for the RAI app only
Section titled “Enable internal stage unloads for the RAI app only”This is Snowflake’s recommended approach.
It applies only to the app’s own stages and doesn’t change PREVENT_UNLOAD_TO_INTERNAL_STAGES for the rest of your account, so your account-wide protection stays in place.
-
Open the RelationalAI app in the Snowflake web interface.
Sign in to the Snowflake web interface (Snowsight) and open the installed RelationalAI Native App.
-
Approve the internal stage unload request.
Review the app’s request to enable internal stage unloads (the
ENABLE_RAI_INTERNAL_STAGE_UNLOADspecification) and approve it. For the exact steps, see Review specifications for an app in the Snowflake documentation.
Disable PREVENT_UNLOAD_TO_INTERNAL_STAGES account-wide
Section titled “Disable PREVENT_UNLOAD_TO_INTERNAL_STAGES account-wide”Ask an ACCOUNTADMIN to disable the parameter for the whole account:
ALTER ACCOUNT SET PREVENT_UNLOAD_TO_INTERNAL_STAGES = FALSE;After activation succeeds, confirm the app is ready by following Check that the RAI service is running.
Share logs with RelationalAI support
Section titled “Share logs with RelationalAI support”Requires the ACCOUNTADMIN role and permission to create and run Snowflake Notebooks in Snowsight.
Use on-demand logs when RelationalAI asks for deeper diagnostics for a specific time window, engine, or incident. This workflow shares a filtered set of staged logs instead of exposing your full telemetry history.
Do the following to create the secure share and copy the requested logs:
-
Download and import the notebook into Snowflake
Download the on-demand logs notebook.
In Snowsight, choose + Create Projects > Notebook > Import .ipynb File, then upload the notebook. If you want to inspect the workflow before importing it, use the embedded preview below.
View a preview of the notebook.
Sharing On-Demand Logs with RAI
Overview
This notebook creates and shares a secure-share view with RAI to provide access to on-demand logs. It’s designed to run as a Snowflake Notebook, but it can be adapted to run as a local Jupyter notebook by replacing the call to
get_active_session()with code to generate aSessionobject locally. See Snowflake’s documentation for details.from datetime import datetime, timedelta, datedef get_date_range(start_date, end_date):start = datetime.strptime(start_date, "%Y-%m-%d")end = datetime.strptime(end_date, "%Y-%m-%d")dates = [(start + timedelta(days=i)).strftime("%Y-%m-%d")for i in range((end - start).days + 1)]return "|".join(dates)Edit this cell with your specific values:
# Override this to the date you want to get logs starting from in the format YYYY-MM-DD:start_date = date.today().strftime("%Y-%m-%d")# Override this to the date you want to get logs until in the format YYYY-MM-DDend_date = date.today().strftime("%Y-%m-%d")date_range = get_date_range(start_date, end_date)# Override this to True if you want to share spcs_control_plane logsinclude_erps_logs = False# Override this to the engine name (ex, 'testEngine') or leave it as it is if you want to get logs for all enginesengine_name = ".*"warehouse = ""# Override this to a unique id and share it with RAIid = ""# The account you want to share the logs with -- it should be in the format org.account_nameevent_sharing_account = ""# Your native app name (usually relationalai)native_app_name = "relationalai"engine_file_pattern = f'{engine_name}/clientlogs-.*({date_range}).*\\.json|{engine_name}/clientlogs-engine.json'erp_file_pattern = f'|clientlogs-cp-({date_range}).*\\.json|.*clientlogs-cp.json'if include_erps_logs:file_pattern = f'.*({engine_file_pattern}{erp_file_pattern}).*'else:file_pattern = f'.*({engine_file_pattern}).*'sql_query = """BEGINUSE ROLE ACCOUNTADMIN;USE WAREHOUSE &warehouse; -- Update to use another warehouse if necessary.CREATE DATABASE IF NOT EXISTS TELEMETRY_SHARING;USE DATABASE TELEMETRY_SHARING;CREATE SCHEMA IF NOT EXISTS LOGS;USE SCHEMA LOGS;--*****---- Load staged data files to temporary tables--*****--CREATE OR REPLACE TABLE TELEMETRY_SHARING.LOGS.TELEMETRY_LOAD_TABLE_&id (LOG_RECORD VARCHAR);CREATE OR REPLACE TABLE TELEMETRY_SHARING.LOGS.TELEMETRY_SHARE_TABLE_&id (TIMESTAMP TIMESTAMP,OBSERVED_TIMESTAMP TIMESTAMP,SPAN_ID VARCHAR,TRACE_ID VARCHAR,MESSAGE VARCHAR,LOG_RECORD VARCHAR);--*****---- Create secure view from table with target log records--*****--CREATE OR REPLACE SECURE VIEW TELEMETRY_SHARING.LOGS.TELEMETRY_SHARE_VIEW_&idCOMMENT = 'View containing telemetry records to share with the RAI provider account'ASSELECT *FROM TELEMETRY_SHARING.LOGS.TELEMETRY_SHARE_TABLE_&id ;--*****---- Share secure view with the RAI provider account--*****--CREATE OR REPLACE SHARE TELEMETRY_SHARE_&id;CREATE DATABASE ROLE IF NOT EXISTS TELEMETRY_SHARE_ROLE;GRANT USAGE ON DATABASE TELEMETRY_SHARING TO SHARE TELEMETRY_SHARE_&id;GRANT USAGE ON SCHEMA TELEMETRY_SHARING.LOGS TO DATABASE ROLE TELEMETRY_SHARE_ROLE;GRANT SELECT ON VIEW TELEMETRY_SHARE_VIEW_&id TO DATABASE ROLE TELEMETRY_SHARE_ROLE;GRANT DATABASE ROLE TELEMETRY_SHARE_ROLE TO SHARE TELEMETRY_SHARE_&id;ALTER SHARE TELEMETRY_SHARE_&id ADD ACCOUNTS = &event_sharing_account;COPY INTO TELEMETRY_SHARING.LOGS.TELEMETRY_LOAD_TABLE_&idFROM (SELECT$1 AS log_recordFROM @&native_app_name.app_state.client_log_stage)PATTERN = '&file_pattern'FILE_FORMAT = (TYPE = JSON)ON_ERROR = CONTINUE; -- This will skip any log records that are invalid JSON.-- The output of the query will indicate if any records were skipped due to errors.-- Copy from TELEMETRY_LOAD_TABLE_&id into TELEMETRY_SHARE_TABLE_&id and remove safe logsINSERT INTO TELEMETRY_SHARING.LOGS.TELEMETRY_SHARE_TABLE_&idSELECTto_timestamp(timeUnixNano) as timestamp,to_timestamp(observedTimeUnixNano) as observed_timestamp,spanId,traceId,a.value:value:stringValue as message,log_recordFROM (SELECTvalue:timeUnixNano as timeUnixNano,value:observedTimeUnixNano as observedTimeUnixNano,value:spanId as spanId,value:traceId as traceId,value as log_recordFROM TELEMETRY_SHARING.LOGS.TELEMETRY_LOAD_TABLE_&id, LATERAL FLATTEN( INPUT => TRY_PARSE_JSON($1):resourceLogs[0]:scopeLogs[0]:logRecords, OUTER => TRUE )),LATERAL FLATTEN( INPUT => log_record:body:kvlistValue:values, OUTER => TRUE) a, LATERAL FLATTEN( INPUT => log_record:attributes, OUTER => TRUE) bWHERE a.VALUE:key = 'message'and(( -- engine unsafe logsb.value:key = 'log.file.name'and b.value:value:stringValue ='engine-unsafe.log')or(-- erps unsafe logslog_record not like '%___safe_to_log%'and log_record not like '%engine-safe.log%'and log_record like '%spcs_control_plane%'));END;"""sql_query = (sql_query.replace('&warehouse', warehouse).replace('&id', id).replace('&event_sharing_account', event_sharing_account).replace('&date_range', date_range).replace('&native_app_name', native_app_name).replace('&file_pattern', file_pattern))# if running as a Snowflake notebook:session = get_active_session()# # if running as a local Jupyter notebook:# from snowflake.snowpark import Session## connection_parameters = {# "account": "<your snowflake account>",# "user": "<your snowflake user>",# "password": "<your snowflake password>",# "role": "<your snowflake role>", # optional# "warehouse": "<your snowflake warehouse>", # optional# "database": "<your snowflake database>", # optional# "schema": "<your snowflake schema>", # optional# }## session = Session.builder.configs(connection_parameters).create()session.sql(sql_query).collect()Run the following cell to confirm that the logs are copied to the secure share:
session.sql("USE ROLE ACCOUNTADMIN").collect()session.sql(f"USE WAREHOUSE {warehouse}").collect()query = f"SELECT * FROM TELEMETRY_SHARING.LOGS.TELEMETRY_SHARE_TABLE_{id} LIMIT 5"session.sql(query).collect() -
Set the notebook inputs
Before you update the configuration cell, gather the values that control which logs are copied and where the secure share is sent:
Input What to decide start_dateandend_dateThe date range to include in the log share. engine_nameA specific engine (i.e., reasoner) name, or .*if RelationalAI asked for logs across all engines.include_erps_logsWhether to include spcs_control_planelogs in addition to engine logs.warehouseThe Snowflake warehouse the notebook should use while building the share. idA unique identifier for this share so the created tables, view, and share do not collide with another run. event_sharing_accountThe target Snowflake account name that RelationalAI gives you for the secure share. native_app_nameThe installed app name, which is usually relationalai.Then update the configuration cell with the scope and sharing details you prepared:
start_date = "YYYY-MM-DD"end_date = "YYYY-MM-DD"include_erps_logs = Falseengine_name = ".*"warehouse = "<warehouse_name>"id = "<unique_share_id>"event_sharing_account = "<org.account_name>"native_app_name = "relationalai" -
Run the notebook to create the secure share
Run the notebook cells in order. The workflow computes the file pattern for your requested date range, creates the
TELEMETRY_SHARING.LOGSobjects, copies matching files from the app stage, filters the records, and grants the resulting secure share to the target account. -
Verify that the share contains the requested logs
Run the verification cell at the end of the notebook, or run the equivalent query below:
session.sql("USE ROLE ACCOUNTADMIN").collect()session.sql(f"USE WAREHOUSE {warehouse}").collect()query = f"SELECT * FROM TELEMETRY_SHARING.LOGS.TELEMETRY_SHARE_TABLE_{id} LIMIT 5"session.sql(query).collect()If the query returns rows, the filtered logs were copied into the share table and the secure share is populated.
Stop the app
Section titled “Stop the app”Requires the app_admin application role.
Stop the app when you are done using it or want to reduce ongoing costs while it is idle.
There are three shutdown modes that achieve different levels of cost reduction by dropping different sets of resources. All modes suspend the app’s CDC service, delete all reasoners, and cancel in-progress jobs. The difference is whether the app service, app warehouse, and compute pools are only suspended or are dropped and recreated the next time you start the app.
Use the following table to choose a shutdown depth before you stop the app:
| What to use | When to use it |
|---|---|
| Stop and suspend managed resources |
|
| Stop, suspend, and remove SPCS service |
|
| Stop and remove all managed resources |
|
Stop the app and suspend resources
Section titled “Stop the app and suspend resources”Choose this option when you want to stop the app but leave the app service, app warehouse, and compute pools in place as suspended resources. This is the lightest shutdown and the quickest option to reverse.
Call the relationalai.app.deactivate() procedure with no arguments:
CALL relationalai.app.deactivate();+------------------------------------------------+| RelationalAI service deactivated successfully. |+------------------------------------------------+Create a Snowpark session from your default PyRel connection with create_config(), then call the relationalai.app.deactivate() SQL procedure with no arguments:
from relationalai.config import create_config
session = create_config().get_session()
session.sql("CALL relationalai.app.deactivate()").collect()Stop the app and remove the SPCS service
Section titled “Stop the app and remove the SPCS service”Choose this option when you want a deeper shutdown that removes the app service but preserves the app warehouse and compute pools as suspended resources. Use it when you want more teardown than the default mode without fully removing the app’s managed infrastructure.
Call the relationalai.app.deactivate() procedure with 'service' as the argument:
CALL relationalai.app.deactivate('service');+------------------------------------------------+| RelationalAI service deactivated successfully. |+------------------------------------------------+Create a Snowpark session from your default PyRel connection with create_config(), then call the relationalai.app.deactivate() SQL procedure with 'service' as the argument:
from relationalai.config import create_config
session = create_config().get_session()
session.sql("CALL relationalai.app.deactivate('service')").collect()Stop the app and drop all managed resources
Section titled “Stop the app and drop all managed resources”Choose this option when you want the deepest shutdown and do not need to keep the app warehouse or compute pools available for the next start. Use it when cost reduction matters more than restart speed.
Call the relationalai.app.deactivate() procedure with 'all' as the argument:
CALL relationalai.app.deactivate('all');+------------------------------------------------+| RelationalAI service deactivated successfully. |+------------------------------------------------+Create a Snowpark session from your default PyRel connection with create_config(), then call the relationalai.app.deactivate() SQL procedure with 'all' as the argument:
from relationalai.config import create_config
session = create_config().get_session()
session.sql("CALL relationalai.app.deactivate('all')").collect()Where to go next
Section titled “Where to go next”- Install and run PyRel once the app is running.
- Upgrade RelationalAI to understand how weekly releases reach your account.
- Understand costs to see what drives app costs.
- Application roles to control who can start, stop, and monitor the app.