I wanted to try out ScyllaDB with Gleam programming language (am beginner) but struggling with establishing connection from gleam. I wasn't able to find any cassandra drivers in gleam so using @external attribute was my only go-to choice in this case.
I used docker to easily initialize 2 scylladb nodes :
services:
gleam:
build:
context: .
dockerfile: Dockerfile-dev
# command:
container_name: server
ports:
- 9163:8080
volumes:
- ./wnw_backend/:/app/wnw_backend
networks:
- web
scylladb-node1:
image: scylladb/scylla:latest
container_name: scylladb-node1
ports:
- '9042:9042'
- '9160:9160'
- '7004:7000'
- '7005:7001'
- '10000:10000'
volumes:
- type: volume
source: target
target: /var/lib/scylla
volume:
nocopy: true
environment:
- SCYLLA_ARGS=--developer-mode 1
healthcheck:
test: ["CMD-SHELL", "cqlsh -e 'SELECT now() FROM system.local' || exit 1"]
start_period: 30s
interval: 10s
timeout: 10s
retries: 10
restart: always
networks:
- web
scylladb-node2:
image: scylladb/scylla:latest
container_name: scylladb-node2
depends_on:
scylladb-node1:
condition: service_healthy
ports:
- '9043:9042'
- '9161:9160'
- '7006:7000'
- '7007:7001'
- '10001:10000'
volumes:
- type: volume
source: target
target: /var/lib/scylla
volume:
nocopy: true
environment:
- SCYLLA_ARGS=--developer-mode 1
command: --seeds=scylladb-node1
healthcheck:
test: ["CMD-SHELL", "cqlsh -e 'SELECT now() FROM system.local' || exit 1"]
start_period: 30s
interval: 10s
timeout: 10s
retries: 10
restart: always
networks:
- web
volumes:
target:
networks:
web:
driver: bridge
# external: true
Code i currently written looks like this :
import app/router
import dot_env as dot
import dot_env/env
import gleam/dynamic.{type Dynamic}
import gleam/erlang/process
import gleam/int
import gleam/io
import mist
import wisp
pub fn main() {
wisp.configure_logger()
dot.new()
|> dot.set_path("/app/.env")
|> dot.set_debug(False)
|> dot.load
let assert Ok(secret_key) = env.get("SECRET_KEY_BASE")
let assert Ok(conn) =
start_link(["scylladb-node1:9042", "scylladb-node2:9042"])
io.println("xandra" <> int.to_string(conn) <> ".")
io.println(secret_key)
let assert Ok(_) =
router.handle_request(_)
|> wisp.mist_handler(secret_key)
|> mist.new()
|> mist.port(8080)
|> mist.start_http()
process.sleep_forever()
}
@external(erlang, "Xandra", "start_link")
pub fn start_link(nodes: List(String)) -> Result(Int, Dynamic)
Compile log:
/app/wnw_backend # gleam run
Compiling wnw_backend
Compiled in 1.45s
Running wnw_backend.main
exception error: undefined function 'Xandra':start_link/1
in function wnw_backend:main/0 (/app/wnw_backend/build/dev/erlang/wnw_backend/_gleam_artefacts/wnw_backend.erl, line 29)/app/wnw_backend #
Now I feel like i might've misunderstood the purpose of the @external attribute. If so, what am i missing?
Instead of directly using the start_link() from elixir, i created new elixir file that encodes strings to atom. And i believe this can be done in gleam as well. I was just too lazy so... anyway, here is my solution :
src/xandra_wrapper.ex
defmodule XandraWrapper do
def start_link(config) do
config_keyword = Enum.map(config, fn {k, v} -> {String.to_atom(k), v} end)
Xandra.start_link(config_keyword)
end
end
src/app/router/cluster.gleam
import gleam/dynamic.{type Dynamic}
@external(erlang, "Elixir.XandraWrapper", "start_link")
pub fn start_link(config: List(#(String, Dynamic))) -> Result(Dynamic, Dynamic)
src/main.gleam
import app/router
import gleam/io
pub fn main() {
...
let config = [#("nodes", dynamic.from(["scylla-node:9042"]))]
let conn = cluster.start_link(config)
io.debug(conn)
...
}
This should at least establish connection and return