blob: bc129c6deed6f072ecbbc84a99a8c604bc6161d6 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Primary-key table changelog (CDC) example.
Subscribe to a primary-key table's changelog and print each row-level change as
a ``+I`` / ``-U`` / ``+U`` / ``-D`` event.
Run standalone against a local cluster:
python example/kv_changelog.py
Or point it at a specific cluster:
FLUSS_BOOTSTRAP_SERVERS=host:port python example/kv_changelog.py
"""
import asyncio
import os
from typing import Optional
import pyarrow as pa
import fluss
DEFAULT_BOOTSTRAP_SERVERS = "127.0.0.1:9123"
async def main(bootstrap_servers: Optional[str] = None):
bootstrap_servers = bootstrap_servers or os.environ.get(
"FLUSS_BOOTSTRAP_SERVERS", DEFAULT_BOOTSTRAP_SERVERS
)
config = fluss.Config({"bootstrap.servers": bootstrap_servers})
conn = await fluss.FlussConnection.create(config)
try:
await _run(conn)
finally:
await conn.close()
print("\nConnection closed")
async def _run(conn):
schema = fluss.Schema(
pa.schema([pa.field("id", pa.int32()), pa.field("name", pa.string())]),
primary_keys=["id"],
)
# A single bucket keeps the changelog on one bucket and in order, which makes
# the CDC output easy to follow.
table_descriptor = fluss.TableDescriptor(schema, bucket_count=1)
admin = conn.get_admin()
table_path = fluss.TablePath("fluss", "example_kv_changelog")
await admin.drop_table(table_path, ignore_if_not_exists=True)
await admin.create_table(table_path, table_descriptor, ignore_if_exists=True)
print(f"Created PK table: {table_path}")
table = await conn.get_table(table_path)
writer = table.new_upsert().create_writer()
# Insert three keys (+I), update one (-U / +U) and delete one (-D).
for id_, name in [(1, "alice"), (2, "bob"), (3, "carol")]:
writer.upsert({"id": id_, "name": name})
writer.upsert({"id": 2, "name": "bob-v2"})
writer.delete({"id": 3})
await writer.flush()
# Subscribe from the start of the changelog and print each CDC event until we
# reach the end of the log.
scanner = await table.new_scan().create_log_scanner()
scanner.subscribe(bucket_id=0, start_offset=fluss.EARLIEST_OFFSET)
print("Changelog (change_type id name):")
while True:
records = await scanner.poll(3000)
if records.is_empty():
break
for record in records:
print(
f" {record.change_type.short_string()} "
f"{record.row['id']} {record.row['name']}"
)
await admin.drop_table(table_path, ignore_if_not_exists=True)
print(f"\nDropped table: {table_path}")
if __name__ == "__main__":
asyncio.run(main())