Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
245 lines
9.8 KiB
Python
245 lines
9.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Drive the SeaweedFS Lance Namespace with LanceDB.
|
|
|
|
The existing client test uses `lance_namespace` and `pylance` directly, which is
|
|
the protocol's reference client. LanceDB is what people actually point at a
|
|
catalog: it connects with `connect_namespace("rest", ...)`, lists what is there,
|
|
opens a table and searches it. This checks the catalog against that path, the
|
|
way the Spark, Trino and ClickHouse suites check the Iceberg one.
|
|
|
|
Everything it prints is either "PASS" or a line starting with FAIL, so the Go
|
|
harness can report the first real failure rather than a stack trace.
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
import warnings
|
|
|
|
warnings.filterwarnings("ignore")
|
|
|
|
import lance
|
|
import lance_namespace as ln
|
|
import lancedb
|
|
import pyarrow as pa
|
|
|
|
DIM = 8
|
|
|
|
|
|
def sample_rows(count):
|
|
"""A vector table, which is the only kind worth putting in Lance."""
|
|
return pa.table(
|
|
{
|
|
"id": pa.array(list(range(count)), type=pa.int64()),
|
|
"title": pa.array([f"row-{i}" for i in range(count)]),
|
|
"vector": pa.array(
|
|
[[float(i) + d for d in range(DIM)] for i in range(count)],
|
|
type=pa.list_(pa.float32(), DIM),
|
|
),
|
|
}
|
|
)
|
|
|
|
|
|
def seed_table(namespace_url, storage, bucket, namespace, table, rows):
|
|
"""Declares a table through the namespace and writes a dataset into it.
|
|
|
|
LanceDB reads through the catalog; the writing half is pylance, because the
|
|
namespace records where a table lives and does not carry its data.
|
|
"""
|
|
ns = ln.connect("rest", {"uri": namespace_url})
|
|
ns.create_namespace(
|
|
ln.CreateNamespaceRequest(id=[bucket], mode="EXIST_OK")
|
|
)
|
|
ns.create_namespace(
|
|
ln.CreateNamespaceRequest(id=[bucket, namespace], mode="EXIST_OK")
|
|
)
|
|
table_id = [bucket, namespace, table]
|
|
declared = ns.declare_table(ln.DeclareTableRequest(id=table_id))
|
|
lance.write_dataset(
|
|
sample_rows(rows), declared.location, storage_options=storage, mode="overwrite"
|
|
)
|
|
print(f"seeded {rows} rows at {declared.location}")
|
|
return declared.location
|
|
|
|
|
|
def check(condition, message):
|
|
if not condition:
|
|
print(f"FAIL: {message}", file=sys.stderr)
|
|
raise SystemExit(1)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--namespace-url", required=True)
|
|
parser.add_argument("--s3-endpoint", required=True)
|
|
parser.add_argument("--bucket", required=True)
|
|
parser.add_argument("--namespace", default="ml")
|
|
parser.add_argument("--table", default="embeddings")
|
|
# Enough rows for an IVF_PQ index to be worth building, which is the point
|
|
# of step 4: without one, a search is a brute-force scan.
|
|
parser.add_argument("--rows", type=int, default=1024)
|
|
parser.add_argument("--access-key", default="any")
|
|
parser.add_argument("--secret-key", default="any")
|
|
args = parser.parse_args()
|
|
|
|
# The namespace vends an endpoint correct for its own host; a container
|
|
# reaches the same gateway by another name, so the endpoint is overridden
|
|
# here and the credentials filled in for a deployment without STS.
|
|
storage = {
|
|
"aws_endpoint": args.s3_endpoint,
|
|
"allow_http": "true",
|
|
"aws_access_key_id": args.access_key,
|
|
"aws_secret_access_key": args.secret_key,
|
|
"aws_region": "us-east-1",
|
|
}
|
|
|
|
location = seed_table(
|
|
args.namespace_url, storage, args.bucket, args.namespace, args.table, args.rows
|
|
)
|
|
|
|
print(f"lancedb {lancedb.__version__} connecting to {args.namespace_url}")
|
|
db = lancedb.connect_namespace(
|
|
"rest", {"uri": args.namespace_url}, storage_options=storage
|
|
)
|
|
|
|
# 1. The catalog is browsable: the bucket is a namespace, and the table is
|
|
# in it under the name the namespace gave it.
|
|
tables = list(db.table_names(namespace_path=[args.bucket, args.namespace], limit=100))
|
|
print(f"table_names -> {tables}")
|
|
check(
|
|
any(args.table in name for name in tables),
|
|
f"{args.table} is not listed in {tables}",
|
|
)
|
|
|
|
# 2. Opening it goes through the catalog: LanceDB asks the namespace where
|
|
# the table is and reads it from there.
|
|
# storage_options is passed per call as well as on the connection: what the
|
|
# namespace vends for a table is merged in, and a deployment without STS
|
|
# vends no credentials, which is what the client would otherwise be left with.
|
|
table = db.open_table(
|
|
args.table,
|
|
namespace_path=[args.bucket, args.namespace],
|
|
storage_options=storage,
|
|
)
|
|
count = table.count_rows()
|
|
print(f"open_table -> {count} rows")
|
|
check(count == args.rows, f"read {count} rows, want {args.rows}")
|
|
|
|
# 3. The schema survived the round trip, vector column included. This is the
|
|
# part a catalog that only records a location cannot fake.
|
|
names = table.schema.names
|
|
print(f"schema -> {names}")
|
|
check("vector" in names and "title" in names, f"schema lost columns: {names}")
|
|
|
|
# 4. Build a vector index, then search it. Both halves matter: an index
|
|
# writes files into a directory of the table the S3 door has to admit -
|
|
# the layout guard has refused a Lance directory before - and without one
|
|
# a search is a brute-force scan that proves nothing about the index path
|
|
# the maintenance worker exists to keep in shape.
|
|
table.create_index(
|
|
metric="l2",
|
|
vector_column_name="vector",
|
|
index_type="IVF_PQ",
|
|
num_partitions=1,
|
|
num_sub_vectors=4,
|
|
)
|
|
indices = table.list_indices()
|
|
print(f"create_index -> {indices}")
|
|
check(len(indices) >= 1, "no index was created")
|
|
|
|
# Vectors are laid out so that id N sits near id N+1, so a query built from
|
|
# id 1 should come back with its neighbourhood. The assertion is a
|
|
# neighbourhood and not an exact id: an IVF_PQ index quantizes, so the
|
|
# nearest hit is approximate by construction - with this data it answers 0
|
|
# as readily as 1, and both are right.
|
|
query = [float(1) + d for d in range(DIM)]
|
|
hits = table.search(query).limit(3).to_list()
|
|
ids = [hit["id"] for hit in hits]
|
|
print(f"search -> {ids}")
|
|
check(len(hits) == 3, f"search returned {len(hits)} hits, want 3")
|
|
check(
|
|
all(i <= 5 for i in ids),
|
|
f"search returned {ids}, which is not the neighbourhood of the query",
|
|
)
|
|
|
|
# 5. A filtered scan, so it is not only the ANN path that works.
|
|
filtered = table.search().where("id < 5").limit(10).to_list()
|
|
print(f"filtered scan -> {len(filtered)} rows")
|
|
check(len(filtered) == 5, f"filter returned {len(filtered)} rows, want 5")
|
|
|
|
# 6. Creating a table. By default LanceDB declares it through the namespace
|
|
# and writes the data itself, which is exactly the split this catalog
|
|
# serves, so this has to work.
|
|
created = db.create_table(
|
|
"created_by_lancedb",
|
|
data=sample_rows(4),
|
|
namespace_path=[args.bucket, args.namespace],
|
|
storage_options=storage,
|
|
)
|
|
check(created.count_rows() == 4, "create_table wrote the wrong number of rows")
|
|
listed = list(db.table_names(namespace_path=[args.bucket, args.namespace], limit=100))
|
|
check(
|
|
any("created_by_lancedb" in name for name in listed),
|
|
f"a table created through LanceDB is not listed: {listed}",
|
|
)
|
|
print(f"create_table -> {created.count_rows()} rows, listed by the catalog")
|
|
|
|
# 7. The same creation with server-side pushdown, which asks the namespace
|
|
# to run CreateTable itself. That operation carries Arrow data and this
|
|
# catalog answers the spec's Unsupported for it. What matters is that the
|
|
# client is left with something coherent either way - it falls back to
|
|
# declare-and-write - rather than a hang, a 404, or a half-made table.
|
|
pushdown = lancedb.connect_namespace(
|
|
"rest",
|
|
{"uri": args.namespace_url},
|
|
storage_options=storage,
|
|
namespace_client_pushdown_operations=["CreateTable"],
|
|
)
|
|
pushed_error = None
|
|
try:
|
|
pushdown.create_table(
|
|
"pushed_by_lancedb",
|
|
data=sample_rows(2),
|
|
namespace_path=[args.bucket, args.namespace],
|
|
storage_options=storage,
|
|
)
|
|
except Exception as err: # noqa: BLE001 - the point is what the client sees
|
|
pushed_error = err
|
|
|
|
after = list(db.table_names(namespace_path=[args.bucket, args.namespace], limit=100))
|
|
landed = any("pushed_by_lancedb" in name for name in after)
|
|
print(f"create_table with pushdown: error={pushed_error!r}, catalog has it={landed}")
|
|
|
|
if pushed_error is None:
|
|
# The client fell back to declare-and-write, so the table is real and
|
|
# has to be readable and complete.
|
|
check(landed, "create_table reported success but the catalog has no table")
|
|
rows = db.open_table(
|
|
"pushed_by_lancedb",
|
|
namespace_path=[args.bucket, args.namespace],
|
|
storage_options=storage,
|
|
).count_rows()
|
|
check(rows == 2, f"the pushed table holds {rows} rows, want 2")
|
|
else:
|
|
# Refused, which is what this catalog answers for a data-plane
|
|
# operation. It has to be that refusal and not some other failure, and
|
|
# it must not have left a half-made table behind.
|
|
message = str(pushed_error).lower()
|
|
check(
|
|
"unsupported" in message or "501" in message or "not implemented" in message,
|
|
f"pushdown failed for an unexpected reason: {pushed_error}",
|
|
)
|
|
check(not landed, "a refused create left a table behind in the catalog")
|
|
|
|
# 8. And the dataset is still readable straight off its URI, which is what
|
|
# keeps the catalog optional.
|
|
direct = lance.dataset(location, storage_options=storage).count_rows()
|
|
check(direct == args.rows, f"direct read got {direct} rows, want {args.rows}")
|
|
print(f"direct read without the catalog -> {direct} rows")
|
|
|
|
print("PASS")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|