Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
78c7091
[SPARK-52780] Add ToLocalIterator and Arrow Record Streaming
caldempsey Jul 13, 2025
5e0a589
[debug] a case where context cancellations result in a panic
caldempsey Jul 13, 2025
c277f5b
[SPARK-52780] fix test compilation
caldempsey Jul 13, 2025
7ce5d47
[SPARK-52780] TestRowIterator_BothChannelsClosedCleanly should EOF (D…
caldempsey Jul 13, 2025
2b6044a
[SPARK-52780] fix linting error
caldempsey Jul 13, 2025
1a897ef
[SPARK-52780] rowiterator.go channel closing should deterministically…
caldempsey Jul 13, 2025
3dcab75
fix: merge
caldempsey Sep 3, 2025
510196f
refactor/7: rename module path to github.com/caldempsey/spark-connect…
caldempsey Apr 19, 2026
6ff1b8d
fix/5: run build workflow on push to main (#6)
caldempsey Apr 19, 2026
9fe788e
fix/8: fall back to archive.apache.org when dlcdn 404s (#9)
caldempsey Apr 19, 2026
1e55022
fix/2: strip DEBUG stdout prints from the ExecutePlan loop (#10)
caldempsey Apr 19, 2026
067c33b
feat/3: expose gRPC transport options on the channel + session builde…
caldempsey Apr 19, 2026
0df2745
feat/4: add typed DataFrame[T] (#12)
caldempsey Apr 19, 2026
73eb884
docs/13: drop upstream "not for production" notice from fork README (…
caldempsey Apr 19, 2026
00ea6cc
feat: top-level typed helpers + Dataset[T] alias (#15)
caldempsey Apr 19, 2026
a95f63e
chore: rename module path to github.com/datalakego/spark-connect-go (…
caldempsey Apr 19, 2026
8cc7f26
feat: Dataset[T].Where / Limit / OrderBy / First / Stream methods (#17)
caldempsey Apr 19, 2026
ff758f5
feat: ErrClusterNotReady + IsClusterNotReady + NewClusterNotReady (#19)
caldempsey Apr 19, 2026
bc0afa6
feat: SqlAs[T] + TableAs[T] free functions; SqlTyped deprecated (#18)
caldempsey Apr 19, 2026
a96e5c4
feat: database/sql driver over Spark Connect (#20)
caldempsey Apr 19, 2026
0304633
refactor: trim typed API to edge-wrapper shape only
caldempsey Apr 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ on:
pull_request:
push:
branches:
- master
- main

env:
SPARK_VERSION: '4.0.0'
Expand Down Expand Up @@ -84,7 +84,12 @@ jobs:
echo "Apache Spark is not installed"
# Access the directory.
mkdir -p ~/deps/
wget -q https://dlcdn.apache.org/spark/spark-${{ env.SPARK_VERSION }}/spark-${{ env.SPARK_VERSION }}-bin-hadoop${{ env.HADOOP_VERSION }}.tgz
# dlcdn.apache.org only keeps current releases on its mirrors and
# occasionally 404s on older ones. archive.apache.org is the
# canonical mirror and never rotates — use it as a fallback.
ARCHIVE=spark-${{ env.SPARK_VERSION }}-bin-hadoop${{ env.HADOOP_VERSION }}.tgz
wget -q https://dlcdn.apache.org/spark/spark-${{ env.SPARK_VERSION }}/$ARCHIVE || \
wget -q https://archive.apache.org/dist/spark/spark-${{ env.SPARK_VERSION }}/$ARCHIVE
tar -xzf spark-${{ env.SPARK_VERSION }}-bin-hadoop${{ env.HADOOP_VERSION }}.tgz -C ~/deps/
# Delete the old file
rm spark-${{ env.SPARK_VERSION }}-bin-hadoop${{ env.HADOOP_VERSION }}.tgz
Expand Down
7 changes: 0 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ This project houses the **experimental** client for [Spark
Connect](https://spark.apache.org/docs/latest/spark-connect-overview.html) for
[Apache Spark](https://spark.apache.org/) written in [Golang](https://go.dev/).

## Current State of the Project

Currently, the Spark Connect client for Golang is highly experimental and should
not be used in any production setting. In addition, the PMC of the Apache Spark
project reserves the right to withdraw and abandon the development of this project
if it is not sustainable.

## Getting started

This section explains how to run Spark Connect Go locally.
Expand Down
2 changes: 1 addition & 1 deletion cmd/spark-connect-example-raw-grpc-client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"log"
"time"

proto "github.com/apache/spark-connect-go/internal/generated"
proto "github.com/datalakego/spark-connect-go/internal/generated"
"github.com/google/uuid"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
Expand Down
8 changes: 4 additions & 4 deletions cmd/spark-connect-example-spark-session/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import (
"fmt"
"log"

"github.com/apache/spark-connect-go/spark/sql/types"
"github.com/datalakego/spark-connect-go/spark/sql/types"

"github.com/apache/spark-connect-go/spark/sql/functions"
"github.com/datalakego/spark-connect-go/spark/sql/functions"

"github.com/apache/spark-connect-go/spark/sql"
"github.com/apache/spark-connect-go/spark/sql/utils"
"github.com/datalakego/spark-connect-go/spark/sql"
"github.com/datalakego/spark-connect-go/spark/sql/utils"
)

var (
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

module github.com/apache/spark-connect-go
module github.com/datalakego/spark-connect-go

go 1.23.2
go 1.24

require (
github.com/apache/arrow-go/v18 v18.4.0
Expand Down
10 changes: 5 additions & 5 deletions internal/tests/integration/dataframe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ import (
"os"
"testing"

"github.com/apache/spark-connect-go/spark/sql/utils"
"github.com/datalakego/spark-connect-go/spark/sql/utils"

"github.com/apache/spark-connect-go/spark/sql/types"
"github.com/datalakego/spark-connect-go/spark/sql/types"

"github.com/apache/spark-connect-go/spark/sql/column"
"github.com/datalakego/spark-connect-go/spark/sql/column"

"github.com/apache/spark-connect-go/spark/sql/functions"
"github.com/datalakego/spark-connect-go/spark/sql/functions"

"github.com/apache/spark-connect-go/spark/sql"
"github.com/datalakego/spark-connect-go/spark/sql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down
6 changes: 3 additions & 3 deletions internal/tests/integration/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import (
"context"
"testing"

"github.com/apache/spark-connect-go/spark/sql/types"
"github.com/datalakego/spark-connect-go/spark/sql/types"

"github.com/apache/spark-connect-go/spark/sql/functions"
"github.com/datalakego/spark-connect-go/spark/sql/functions"

"github.com/apache/spark-connect-go/spark/sql"
"github.com/datalakego/spark-connect-go/spark/sql"
"github.com/stretchr/testify/assert"
)

Expand Down
2 changes: 1 addition & 1 deletion internal/tests/integration/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/apache/arrow-go/v18/arrow/array"
"github.com/apache/arrow-go/v18/arrow/memory"

"github.com/apache/spark-connect-go/spark/sql"
"github.com/datalakego/spark-connect-go/spark/sql"
)

func connect() (context.Context, sql.SparkSession) {
Expand Down
2 changes: 1 addition & 1 deletion internal/tests/integration/spark_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"os/exec"
"time"

"github.com/apache/spark-connect-go/spark/sparkerrors"
"github.com/datalakego/spark-connect-go/spark/sparkerrors"
)

func StartSparkConnect() (int64, error) {
Expand Down
8 changes: 4 additions & 4 deletions internal/tests/integration/sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import (
"os"
"testing"

"github.com/apache/spark-connect-go/spark/sql/column"
"github.com/datalakego/spark-connect-go/spark/sql/column"

"github.com/apache/spark-connect-go/spark/sql/functions"
"github.com/datalakego/spark-connect-go/spark/sql/functions"

"github.com/apache/spark-connect-go/spark/sql/types"
"github.com/datalakego/spark-connect-go/spark/sql/types"

"github.com/apache/spark-connect-go/spark/sql"
"github.com/datalakego/spark-connect-go/spark/sql"
"github.com/stretchr/testify/assert"
)

Expand Down
7 changes: 4 additions & 3 deletions spark/client/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ package base
import (
"context"

"github.com/apache/spark-connect-go/spark/sql/utils"
"github.com/datalakego/spark-connect-go/spark/sql/utils"

"github.com/apache/arrow-go/v18/arrow"
"github.com/apache/spark-connect-go/internal/generated"
"github.com/apache/spark-connect-go/spark/sql/types"
"github.com/datalakego/spark-connect-go/internal/generated"
"github.com/datalakego/spark-connect-go/spark/sql/types"
)

type SparkConnectRPCClient generated.SparkConnectServiceClient
Expand All @@ -48,5 +48,6 @@ type SparkConnectClient interface {

type ExecuteResponseStream interface {
ToTable() (*types.StructType, arrow.Table, error)
ToRecordBatches(ctx context.Context) (<-chan arrow.Record, <-chan error, *types.StructType)
Properties() map[string]any
}
38 changes: 36 additions & 2 deletions spark/client/channel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ import (
"strconv"
"strings"

"github.com/apache/spark-connect-go/spark"
"github.com/datalakego/spark-connect-go/spark"

"github.com/google/uuid"

"google.golang.org/grpc/credentials/insecure"

"github.com/apache/spark-connect-go/spark/sparkerrors"
"github.com/datalakego/spark-connect-go/spark/sparkerrors"
"golang.org/x/oauth2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
Expand Down Expand Up @@ -77,6 +77,28 @@ type BaseBuilder struct {
headers map[string]string
sessionId string
userAgent string
dialOpts []grpc.DialOption
}

// defaultMaxMessageSize is the per-RPC send/receive ceiling we apply
// when the caller doesn't set one. The server advertises
// spark.connect.grpc.maxInboundMessageSize at 128 MiB by default and
// operators routinely raise it to 1 GiB for bulk reads. gRPC's own
// default is 4 MiB, which silently truncates a single Arrow batch on
// any non-trivial query and surfaces as opaque ResourceExhausted
// errors deep inside Collect()/StreamRows(). Matching the upper end
// of the server's practical range keeps first-run queries from
// hitting a floor the caller didn't know existed.
const defaultMaxMessageSize = 1 << 30 // 1 GiB

// WithDialOptions appends gRPC dial options to the channel builder.
// Callers use this to raise the per-call message ceiling further, set
// keepalive parameters for long-lived streams, inject interceptors,
// or swap in a custom dialer. Options are applied after the builder's
// defaults, so caller-supplied values win.
func (cb *BaseBuilder) WithDialOptions(opts ...grpc.DialOption) *BaseBuilder {
cb.dialOpts = append(cb.dialOpts, opts...)
return cb
}

func (cb *BaseBuilder) Host() string {
Expand Down Expand Up @@ -114,6 +136,15 @@ func (cb *BaseBuilder) Build(ctx context.Context) (*grpc.ClientConn, error) {
var opts []grpc.DialOption

opts = append(opts, grpc.WithAuthority(cb.host))

// Raise the per-call send/receive ceiling off gRPC's 4 MiB default.
// Placed before WithDialOptions so caller-supplied DefaultCallOptions
// can still override if they want a tighter limit.
opts = append(opts, grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(defaultMaxMessageSize),
grpc.MaxCallSendMsgSize(defaultMaxMessageSize),
))

if cb.token == "" {
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
} else {
Expand All @@ -134,6 +165,9 @@ func (cb *BaseBuilder) Build(ctx context.Context) (*grpc.ClientConn, error) {
opts = append(opts, grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: ts}))
}

// Caller overrides come last so they win over the defaults above.
opts = append(opts, cb.dialOpts...)

remote := fmt.Sprintf("%v:%v", cb.host, cb.port)
conn, err := grpc.NewClient(remote, opts...)
if err != nil {
Expand Down
30 changes: 28 additions & 2 deletions spark/client/channel/channel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import (
"testing"

"github.com/google/uuid"
"google.golang.org/grpc"

"github.com/apache/spark-connect-go/spark/client/channel"
"github.com/apache/spark-connect-go/spark/sparkerrors"
"github.com/datalakego/spark-connect-go/spark/client/channel"
"github.com/datalakego/spark-connect-go/spark/sparkerrors"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -112,3 +113,28 @@ func TestChannelBulder_UserAgent(t *testing.T) {
assert.True(t, strings.Contains(cb.UserAgent(), "spark/"))
assert.True(t, strings.Contains(cb.UserAgent(), "os/"))
}

// TestChannelBuilder_WithDialOptions_CompilesAndBuilds is a smoke
// test: the builder accepts caller-supplied grpc.DialOption values,
// stores them, and a subsequent Build produces a live ClientConn
// without panicking. We assert the returned conn is non-nil rather
// than exercising the wire — grpc.NewClient is lazy and doesn't dial
// until the first RPC, so a stricter assertion would spin up a
// server here.
func TestChannelBuilder_WithDialOptions_CompilesAndBuilds(t *testing.T) {
cb, err := channel.NewBuilder("sc://localhost")
assert.NoError(t, err)

// Representative knobs: raise the per-call ceiling further than
// the builder's own default, and give the connection a keepalive
// profile. Neither changes observable behaviour in this unit
// test, but both have to survive the builder round-trip.
cb.WithDialOptions(
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(2<<30)),
grpc.WithUserAgent("dorm-test"),
)

conn, err := cb.Build(context.Background())
assert.NoError(t, err)
assert.NotNil(t, conn)
}
Loading