Spark и Rust: Дополнение

Некоторые время назад мы уже катались темы использования языка Rust в Apache Spark приложениях при помощи UDF. Но если вам не нужна вся мощь типов - то есть более простой подход запустить Rust код. Этим методом является - pipe(). Ок, я немного приврал в заголовке, так как этот подход можно применять с любой программой.

По сути pipe() передает содержимое RDD в stdin переданной программе и читает ее stdout . В документации присутствует исчерпывающий пример:

sc.parallelize(['1', '2', '', '3']).pipe('cat').collect()
# ['1', '2', '', '3']

Но ничего не запрещает вместо cat использовать собственную программу. Ниже небольшой код на Rust, который берет строку и преобразует ее в строку JSON.

use serde::{Deserialize, Serialize};
use std::io::{self, BufRead};

#[derive(Serialize, Deserialize)]
struct Block {
    bar: String,
}

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let block = Block { bar: line.unwrap() };

        println!("{}", serde_json::to_string(&block).unwrap());
    }
}

Не стоит забывать, что при работе в кластерном режиме, каждая из нод кластера должна иметь копию исполняемой программы. Тот же cat есть почти в каждый unix-системе, но нашу программу нужно еще доставить. Это можно сделать в ручную, или “запеч” в образ при создании ноды, или использовать --files / addFile().

# spark-submit --files /path/to/my-rust-bin ...

df = sc.parallelize(['1', '2', '', '3']).pipe('my-rust-bin').toDf()

df.show()

Безусловно этот подход проще, чем написание UDF, но он также имеет ряд недостатков: во-первых, нужно помнить о компиляции кода под разные платформы, во-вторых если выработает с DataFrame, то использование RDD - снизит производительность. Но так или иначе это еще один вариант оптимизации Spark приложений.