Skip to content

Add RayforceDB support - #212

Open
protocolstardust wants to merge 1 commit into
timestored:masterfrom
protocolstardust:rayforce-support
Open

protocolstardust wants to merge 1 commit into
timestored:masterfrom
protocolstardust:rayforce-support

Conversation

@protocolstardust

Copy link
Copy Markdown

Summary

Adds RayforceDB as a server type. Rayforce has no JDBC driver, so qStudio talks its native IPC protocol (wire version 3) the same way it talks to kdb, and decodes replies into the same Java shapes kx.c produces, so the result grid, charts, export and pivot work without changes.

  • New com.timestored.rayforce package: RayIpc (handshake with optional credentials, framing, run-length/delta decompression, decoding of every Rayforce type) and RayConnection (reconnect-once, like KdbConnection). Both kdb and Rayforce connections now implement a small NativeConnection interface.
  • JdbcTypes.RAYFORCE with icon, ;; line comments, default port 5000 and the existing .rfl language.
  • Server tree built from one Rayfall expression over (env 0), filtering builtins. Tables preview as a grid, other globals show their value.
  • Generated queries (top/bottom 1000, count, pivot) use select with a take: clause, because the take builtin cycles on tables shorter than the count. Pivots are grouped and ordered server-side and finished client-side like other non-PIVOT databases.
  • Console shows text the query printed on the server; errors surface the Rayforce error code.
  • The "query maximum size limit" preference is enforced client-side for Rayforce replies, draining oversized frames so the connection stays usable.
  • AI query prompt for Rayfall.

Verification

  • RayIpcTest drives the client against a fake server that speaks the wire format: handshake, auth, version mismatch, compression, typed nulls, tables, errors, console capture.
  • Manually verified against a real Rayforce server: every atom and vector type including nulls, dates, timestamps, GUIDs and strings; a 20k-row table (compressed frames); dicts, nested lists, lambdas; printed output; error codes; the tree listing; every generated query; client-side pivot; the size limit.

Known limitation

Only the editor query path and the server tree use the native protocol. SQL notebooks, BabelDB query_db and dashboard charts still go through the JDBC pool, which cannot serve a Rayforce server.

Connect to RayforceDB over its own IPC protocol (wire version 3), with no
JDBC driver: handshake with optional credentials, framed messages,
run-length/delta decompression, and decoding of every Rayforce type into
the same Java shapes kdb results use, so the grid, charts, export and
pivot all work unchanged.

- New com.timestored.rayforce package: RayIpc client and RayConnection,
  plus a NativeConnection interface shared with KdbConnection.
- Rayforce entry in JdbcTypes with icon, ;; comments and .rfl language.
- Server tree built from one Rayfall expression over (env 0), filtering
  builtins; table previews and value previews over the native protocol.
- Generated queries (top/bottom 1000, count, pivot) use select with a
  take: clause since the take builtin cycles on short tables; pivot is
  finished client-side with ordered grouping.
- Console shows output the query printed server-side; errors carry the
  Rayforce error code.
- Query maximum size limit enforced client-side for Rayforce replies.
- AI query prompt for Rayfall.
- RayIpcTest drives the client against a fake server speaking the wire
  format.
@ryanhamilton

Copy link
Copy Markdown
Collaborator

Hi @protocolstardust

I only had a quick look and PR looks mostly good.

My main question would be, can we use a standard JDBC driver?

  • If rayforce provided a JDBC driver it would allow QStudio and many other tools to reuse that same interface.
  • My worry with this QStudio specific IPC is that it leaves QStudio responsible for maintenance and updates.

The server connection logic is 95% shared between Pulse (Our closed commercial product) and QStudio. Another fairly standard JDBC driver I can add relatively easy to both. To add this custom logic I'll need to go try and do the same to Pulse , which itself has many modifications as it's trying to support a large server deploy rather than one person querying. Because the changes are intermingled with the kdb connection work which is heavily relied upon and has been tweaked over many years, together with issue 4. it massively increases the risk to me. I would need to work with the large companies that have Pulse deployed and notify them, their connection handling may change and to be vigilant.

I'm not against a native connection but I think we should be able to justify our decision why we used that.

* shipping the values themselves. {@code env} lists builtins too; they and
* user lambdas both have type {@code ?}, and only formatting tells them apart.
*/
public static final String RAYFORCE_LISTING_QUERY = "((fn [e] (table [name type len cols] (list (key e)"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@singaraiona Is this listing query stable? i.e. Should it work forever? and/or is there a standard API call to fetch structured data on all objects?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, this shouldn't rely on env plus formatting tricks. There's no structured introspection call in Rayforce today, so I'll add a builtin that returns exactly this table (name, type, length, table columns) as a stable API and we'll switch qStudio to it. The current query will keep working in the meantime.


if(serverConfig.isRayforce()) {
// No JDBC driver to pool - test the way we will actually query.
try {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without connection pooling this driver won't work in Pulse:
https://www.timestored.com/pulse/integrations/
We use 90% the same driver code and I've never added a database to one without adding it to the other.
I think adding as is here would break Pulse.

if(k != null) {
rs = jdbc.getRS(k, queryTitle);
}
// kdb pivots server-side; Rayforce only groups, so pivot here like plain SQL databases.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker to this PR but
@anton - Maybe something worth adding?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually Rayforce already has a server-side pivot ((pivot table index pivot_col value_col agg_fn), multi-index supported), so this comment is stale. Not a Rayforce change, we just need to wire the Rayforce path in qStudio to use it instead of pivoting client-side. Happy to do that in a follow-up.

@ryanhamilton

Copy link
Copy Markdown
Collaborator

@singaraiona QStudio (not visible in this repo) uses Testcontainer to spin up databases e.g. for QuestDB:
https://testcontainers.com/modules/questdb/
Would you like to publish an official rayforce container?
I can probably create a generic container of some sort given how few dependencies rayforce has. Just asking as it's the better solution long term. It allows users downstream to test easier.

@singaraiona

Copy link
Copy Markdown

@ryanhamilton Yes, we'll publish an official image ourselves rather than have a generic one drift from releases.

Plan: ghcr.io/singaraiona/rayforce, multi-arch (linux/amd64 + linux/arm64), built from the release pipeline and tagged per version plus latest. The binary only depends on libc/libm/pthread, so the image will be tiny. Default entrypoint listens on port 5000, so a plain TCP wait strategy is enough for Testcontainers.

I'll link it here once it's published.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants