Apache Spark Backend

Download Spark from https://spark.apache.org/downloads.html and install it. Spark provides startup scripts for UNIX operating systems (not Windows).

Install chronify with Spark support

$ pip install "chronify[spark]"

Installation on a development computer

Installation can be as simple as

$ tar -xzf spark-4.0.1-bin-hadoop3.tgz
$ export SPARK_HOME=$(pwd)/spark-4.0.1-bin-hadoop3

Installation on an HPC

The chronify development team uses this package to run Spark on NLR’s HPC.

Chronify Usage

This example creates a chronify Store with Spark as the backend and then adds a view to a Parquet file. Chronify will run its normal time checks.

First, create the Parquet file and chronify schema.

from datetime import datetime, timedelta

import numpy as np
import pandas as pd
from chronify import DatetimeRange, Store, TableSchema, CsvTableSchema

initial_time = datetime(2020, 1, 1)
end_time = datetime(2020, 12, 31, 23)
resolution = timedelta(hours=1)
timestamps = pd.date_range(initial_time, end_time, freq=resolution, unit="us")
dfs = []
for i in range(1, 4):
    df = pd.DataFrame(
        {
            "timestamp": timestamps,
            "id": i,
            "value": np.random.random(len(timestamps)),
        }
    )
    dfs.append(df)
df = pd.concat(dfs)
df.to_parquet("data.parquet", index=False)
schema = TableSchema(
    name="devices",
    value_column="value",
    time_config=DatetimeRange(
        time_column="timestamp",
        start=initial_time,
        length=len(timestamps),
        resolution=resolution,
    ),
    time_array_id_columns=["id"],
)
from chronify import Store
from chronify.ibis.spark_backend import SparkBackend

store = Store(backend=SparkBackend())
store.create_view_from_parquet("data.parquet", schema)

Alternatively, pass a pre-configured PySpark session:

from pyspark.sql import SparkSession

session = SparkSession.builder.master("local").getOrCreate()
store = Store(backend=SparkBackend(session=session))

Warehouse directory

Spark stores the tables that chronify creates, including the intermediate tables it builds while mapping time, in its warehouse directory. Spark defaults to spark-warehouse in the current working directory. On a multi-node cluster, such as an HPC, the warehouse must be on a filesystem that every node can access, not on node-local storage like /tmp.

Set it with warehouse_dir:

store = Store(backend=SparkBackend(warehouse_dir="/scratch/my_user/spark-warehouse"))

If you pass your own session, set spark.sql.warehouse.dir when you build it instead. warehouse_dir cannot be combined with session:

session = (
    SparkSession.builder.master("local")
    .config("spark.sql.warehouse.dir", "/scratch/my_user/spark-warehouse")
    .getOrCreate()
)
store = Store(backend=SparkBackend(session=session))

Spark fixes the warehouse location when a session starts. If a session is already running with a different warehouse, SparkBackend(warehouse_dir=...) raises an error. Stop that session first.

Note

This replaces the scratch_dir argument from chronify 0.7 and earlier, which controlled where the Hive backend wrote temporary Parquet files.

Verify the data:

store.read_table(schema.name).execute().head()
            timestamp  id     value
0 2020-01-01 00:00:00   1  0.785399
1 2020-01-01 01:00:00   1  0.102756
2 2020-01-01 02:00:00   1  0.178587
3 2020-01-01 03:00:00   1  0.326194
4 2020-01-01 04:00:00   1  0.994851

Time configuration mapping

The primary use case for Spark is to map datasets that are larger than can be processed by DuckDB on one computer. In such a workflow a user would call

store.map_table_time_config(src_table_name, dst_schema, output_file="mapped_data.parquet")