Spanner Omni ile Cihaz Üzerinde Veri Temsilcileri ve GraphRAG

1. Genel Bakış

Bu codelab'de Spanner Omni'nin temelleri, otonom yapay zeka ajanları için neden en iyi veri temeli olduğu ve Spanner Omni (İlişkisel + Grafik GQL), Python Flask ve Gemini 3.8 Flash tarafından desteklenen, uçta dağıtılmış, temsilci tabanlı bir Pickleball taktik koçu ve maç simülatörünün kaba bir uçtan uca uygulaması ele alınmaktadır.

Sorun

Modern etkileşimli spor platformları ve gerçek zamanlı oyun ortamları, aynı anda üç farklı iş yükünün işlenmesini gerektirir:

Yüksek eşzamanlı operasyonel işlemler:

Çift rezervasyon çakışmalarını ortadan kaldırmak için katı ACID serileştirilebilirliği gerektiren kort rezervasyonları, oyuncu üyelikleri, faturalandırma ve turnuva programları.

Bağlı Grafik Telemetrisi:

Sürekli ralli vuruş dizileri, geçiş Markov zincirleri ve hantal ETL ardışık düzenleri olmadan düşük gecikmeli geçiş gerektiren bölge kapsamı grafikleri.

Milisaniyenin Altında Uç Yanıt Süresi:

Geniş alan bulut ağı gidiş dönüşlerinin (100-200 ms) kabul edilemez gecikmelere neden olacağı hızlı tempolu 60 FPS fizik simülasyonları.

Geçmişte mühendislik ekipleri bu sorunu, parçalı mimarileri bir araya getirerek çözüyordu. Bu mimariler şuna benziyordu: İşlem verileri için PostgreSQL, kısa grafikler için Neo4j, uç önbelleğe alma için Redis ve arka plan senkronizasyon ardışık düzenleri. Bu durum, mimari sapmaya, yüksek operasyonel karmaşıklığa ve nihai tutarlılık gecikmelerine neden olur.

Nasıl yapıyoruz?

Google Cloud Spanner Omni'yi uçta dağıtıp Google Gemini 3.8 Flash ile birleştirerek Spanner Omni Pickleball Game'i geliştirdik. Bu, ilişkisel iş mantığını, yerel ISO GQL vuruş telemetrisini ve gerçek zamanlı taktiksel yapay zeka koçluğunu birleştiren tek motorlu bir spor oyun platformudur.

Spanner Omni

Spanner Omni; Google'ın gezegen ölçeğinde dağıtılmış veritabanı motorunu yatay ölçeklenebilirlik, ACID işlemleri ve çoklu model özellikleri (ilişkisel, grafik, vektör ve analiz) ile birlikte kendi kendine yönetilen, şirket içi, çoklu bulut ve hava boşluklu ortamlara taşır. Spanner Omni, Google Cloud Spanner'ın indirilebilir ve kendi kendine yönetilen bir sürümüdür. Spanner'ın temel dağıtılmış veritabanı özelliklerini (otomatik parçalama, Paxos tabanlı replikasyon ve güçlü harici tutarlılık) çalıştırmayı seçtiğiniz her yerde sunar:

acc871a470f63b29.png

Neler öğreneceksiniz?

Bu codelab'de, geleneksel silolu veritabanı mimarilerinden modern ve birleştirilmiş bir uç veritabanı çözümüne geçiş yapacaksınız. Öğrenecekleriniz:

  1. Yüksek performanslı, tek örnekli ilişkisel iş yüklerini yönetmek için Docker'ı kullanarak Spanner Omni'yi yerel olarak dağıtın ve yönetin.
  2. Harici veritabanı senkronizasyonu gerektirmeden, işlemsel iş modellerini yüksek boyutlu oyun telemetrisiyle eşleyen bir Birleşik İlişkisel ve ISO GQL Grafik Şeması tasarlayın.
  3. Yarışma durumlarını programatik olarak önlemek ve çifte rezervasyon çakışmalarını ortadan kaldırmak için Python okuma-yazma işlemlerinde katı ACID serileştirilebilirliği uygulayın.
  4. SQL ile entegre ISO GQL grafik sorgularından yararlanarak gerçek zamanlı ve bağlamsal olarak temellendirilmiş maç telemetrisini doğrudan Google Gemini'a aktaran ve anında taktiksel koçluk sağlayan yerel bir GraphRAG ardışık düzeni oluşturun.

Şartlar

  • Linux (Ubuntu 22.04+), macOS veya Windows çalıştıran standart bir yerel makine ya da geliştirme sanal makinesi.
  • Docker Desktop veya Docker Engine yüklü ve çalışıyor olmalıdır.
  • Python 3.10 veya sonraki bir sürümün yüklü olması gerekir.
  • Metin düzenleyici veya IDE (ör. VS Code veya PyCharm).
  • Etkin bir Google Gemini API anahtarı. (Not: Spanner Omni, ücretsiz katman kapsamında kendi kendine yönetilen bir geliştirici değerlendirme lisansı altında çalıştığından, Spanner Omni'yi yerel olarak çalıştırmak için Google Cloud Faturalandırma Hesabı gerekmez.)

2. Spanner Omni Dağıtımı ve Yapılandırması

Bu adımda, yerel uç veritabanı örneğimizi oluşturacağız. Spanner Omni, yüksek düzeyde optimize edilmiş bir container içinde çalışarak yerel geliştirici dizüstü bilgisayarınızda dünya ölçeğinde ilişkisel özellikler sunar.

  1. Terminalinizi açın ve veritabanı şemalarınız ile oyun telemetrinizi depolamak için kalıcı bir Docker birimi oluşturun:
docker volume create spanner_omni_data
  1. Tek sunuculu geliştirici modunda yapılandırılmış resmi Spanner Omni container görüntüsünü alın ve başlatın:
docker run -d --name spanner-omni \
  -p 9010:9010 -p 9020:9020 \
  -v spanner_omni_data:/spanner \
  us-docker.pkg.dev/spanner-omni/images/spanner-omni:2026.r2.1-beta \
  start-single-server
  1. Kapsayıcının başarıyla başlatıldığından emin olun:
docker ps

🛠️ Tek satırlık hızlı sorun giderme: 9010 veya 9020 bağlantı noktaları arka plan işlemleri ya da eski emülatör çalıştırmaları tarafından kullanıldığı için başlatma işlemi başarısız olursa terminalinizde şu temizleme komut dosyasını çalıştırın:

pkill -f "spanner" || true
fuser -k 9010/tcp 9020/tcp 2>/dev/null || true
docker rm -f spanner-local spanner-omni spanner-omni-local 2>/dev/null || true

3. İlişkisel ve ISO GQL Grafik DDL Şema Tasarımı

Spanner Omni etkinleştirildikten sonra veritabanı şemamızı oluşturabiliriz. Bu şema, ilişkisel ticari işletmeleri (ör. sahalar, oyuncular ve rezervasyonlar) hızlı, bağlantılı grafik telemetri şemalarıyla (şutlar, bölgeler ve geçişler) birleştirerek tek doğruluk kaynağımız olarak işlev görür.

İşlem ve telemetri tablolarını oluşturmak için:

  1. Spanner CLI'yı veya uygulama istemcinizi kullanarak veritabanınıza bağlanma
  2. Aşağıdaki DDL ifadelerini çalıştırın

Bu sorunu uygulama kodunda ele aldığımız için şu anda herhangi bir işlem yapmanız gerekmiyor:

-- ============================================================================
-- 1. OPERATIONAL & FACILITY MANAGEMENT (RELATIONAL ACID)
-- ============================================================================

CREATE TABLE Courts (
    court_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    location STRING(100) NOT NULL,
    surface_type STRING(50) NOT NULL,
    is_indoor BOOL NOT NULL,
    hourly_rate FLOAT64 NOT NULL,
    has_lighting BOOL NOT NULL,
    created_at TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (court_id);

CREATE TABLE Players (
    player_id INT64 NOT NULL,
    name STRING(100) NOT NULL,
    email STRING(100) NOT NULL,
    skill_level STRING(20) NOT NULL,
    phone STRING(50),
    created_at TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (player_id);

CREATE TABLE Bookings (
    booking_id INT64 NOT NULL,
    court_id INT64 NOT NULL,
    player_id INT64 NOT NULL,
    booking_date DATE NOT NULL,
    start_hour INT64 NOT NULL,
    end_hour INT64 NOT NULL,
    player_name STRING(100) NOT NULL,
    court_name STRING(100) NOT NULL,
    status STRING(20) NOT NULL,
    notes STRING(MAX),
    created_at TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (booking_id);

CREATE TABLE MatchRequests (
    match_id INT64 NOT NULL,
    player_id INT64 NOT NULL,
    player_name STRING(100) NOT NULL,
    court_id INT64,
    court_name STRING(100),
    play_date DATE NOT NULL,
    start_hour INT64 NOT NULL,
    skill_required STRING(20) NOT NULL,
    match_type STRING(20) NOT NULL,
    spots_open INT64 NOT NULL,
    status STRING(20) NOT NULL,
    notes STRING(MAX),
    created_at TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (match_id);

-- ============================================================================
-- 2. TACTICAL GAME TELEMETRY & PROPERTY GRAPH (ISO GQL)
-- ============================================================================

CREATE TABLE CourtZones (
    zone_id STRING(50) NOT NULL,
    name STRING(100) NOT NULL,
    is_kitchen BOOL NOT NULL,
    side STRING(50) NOT NULL
) PRIMARY KEY (zone_id);

CREATE TABLE Shots (
    shot_id STRING(50) NOT NULL,
    player_id STRING(50) NOT NULL,
    shot_type STRING(100) NOT NULL,
    speed_mph FLOAT64,
    spin_type STRING(50),
    outcome STRING(50),
    from_zone_id STRING(50),
    to_zone_id STRING(50)
) PRIMARY KEY (shot_id);

CREATE TABLE ShotTransitions (
    transition_id STRING(50) NOT NULL,
    from_shot_id STRING(50) NOT NULL,
    to_shot_id STRING(50) NOT NULL,
    rally_depth INT64,
    pressure_level STRING(20)
) PRIMARY KEY (transition_id);

-- ============================================================================
-- 3. MATCH SESSIONS & AI COACHING HISTORY
-- ============================================================================

CREATE TABLE GameSessions (
    session_id STRING(50) NOT NULL,
    player_id INT64 NOT NULL,
    player_score INT64 NOT NULL,
    bot_score INT64 NOT NULL,
    bot_difficulty STRING(20) NOT NULL,
    winner STRING(20) NOT NULL,
    rally_length INT64 NOT NULL,
    fault_type STRING(100),
    coaching_summary STRING(MAX),
    created_at TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true)
) PRIMARY KEY (session_id);
cat << 'EOF' > Dockerfile
FROM ollama/ollama:latest
ENV OLLAMA_HOST=0.0.0.0:8080
ENV OLLAMA_MODELS=/root/.ollama
ENV MODEL=gemma4:e2b
RUN ollama serve & sleep 5 && ollama pull $MODEL
ENTRYPOINT ["ollama", "serve"]
EOF

💡 Onay Zaman Damgaları Neden Önemlidir? OPTIONS (allow_commit_timestamp=true) değerini belirtmek, tüm yerel düğüm replikalarında monoton, mikrosaniye hassasiyetinde sıralama yapılmasını sağlar. Bu, yüksek hızlı oyun etkinliklerini (ör. çizgi hakemi video görüntüleri) ilgili veritabanı şut telemetri günlükleriyle senkronize etmek için çok önemlidir.

4. ACID eşzamanlılık kontrolü ve çifte rezervasyon önleme

Aktif spor rezervasyon portallarında, birden fazla kullanıcı genellikle aynı kort zaman aralıklarını rezerve etmek için yarışır. Yarışma durumlarını önlemek için Spanner Omni'de Python okuma-yazma işlemi yazarak mutlak, çift rezervasyon korumalı seri hale getirilebilirlik sağlayacağız.

Şu anda herhangi bir işlem yapmanız gerekmiyor, yalnızca kodu inceleyin:

from google.cloud import spanner

def create_booking_transaction(transaction, booking_data):
    # 1. Query for existing overlapping confirmed bookings on the same court
    query = """
        SELECT booking_id, player_name, start_hour, end_hour
        FROM Bookings
        WHERE court_id = @court_id
          AND booking_date = @booking_date
          AND status = 'CONFIRMED'
          AND NOT (end_hour <= @start_hour OR start_hour >= @end_hour)
    """
    params = {
        "court_id": booking_data["court_id"],
        "booking_date": booking_data["booking_date"],
        "start_hour": booking_data["start_hour"],
        "end_hour": booking_data["end_hour"],
    }
    param_types = {
        "court_id": spanner.param_types.INT64,
        "booking_date": spanner.param_types.DATE,
        "start_hour": spanner.param_types.INT64,
        "end_hour": spanner.param_types.INT64,
    }
    
    results = list(transaction.execute_sql(query, params=params, param_types=param_types))
    
    if results:
        conflict = results[0]
        raise ValueError(
            f"Court conflict: Already booked by {conflict[1]} ({conflict[2]}:00-{conflict[3]}:00)"
        )
    
    # 2. Insert new confirmed booking with Spanner commit timestamp
    insert_stmt = """
        INSERT INTO Bookings (
            booking_id, court_id, player_id, booking_date, start_hour, end_hour,
            player_name, court_name, status, notes, created_at
        ) VALUES (
            @booking_id, @court_id, @player_id, @booking_date, @start_hour, @end_hour,
            @player_name, @court_name, 'CONFIRMED', @notes, PENDING_COMMIT_TIMESTAMP()
        )
    """
    # execute_update statement writing to Spanner Omni...
    transaction.execute_update(insert_stmt, params=booking_data, param_types=booking_types)

5. Gerçek Zamanlı 60 FPS Fizik ve Ayarlanabilir DUPR Bot Motoru

Uygulamamızın ön ucu, saniyede 60 kare (FPS) hızında oluşturulan 2,5D HTML5 Canvas simülasyonu kullanır. Bu oluşturma işlemi, akıcı bir performans sağlamak için veritabanı katmanlarından tamamen bağımsız olarak çalışır ve telemetriyi yalnızca ralliler bittiğinde temizler.

Simülatörde, gerçek dünyadaki DUPR (Dynamic Universal Pickleball Rating) seviyelerine dayalı bir zorluk matrisinden yararlanan ayarlanabilir bir OmniBot yapay zeka motoru bulunur.

1640056a824b47e6.png

Şu anda herhangi bir işlem yapmanız gerekmiyor, yalnızca ayrıntıları inceleyin.

6. ISO GQL Shot Telemetri Grafiği İşleme

Her rallinin sonunda, ön uç, bağlı yörünge grafiğini arka uç uç noktamız olan /api/rally-end'e gönderir. Her çekim ilişkisel bir düğüm olarak kaydedilir ve sekanslar geçiş kenarları aracılığıyla dinamik olarak bağlanır.

Şu anda sizin tarafınızdan herhangi bir işlem yapılması gerekmiyor, yalnızca kodu inceleyin:

@app.route("/api/execute-shot", methods=["POST"])
def execute_shot():
    data = request.json or {}
    shot_id = f"shot_{int(time.time()*1000)}"
    
    # 1. Insert the Shot Node
    insert_shot_sql = """
        INSERT INTO Shots (shot_id, player_id, shot_type, speed_mph, spin_type, outcome, from_zone_id, to_zone_id)
        VALUES (@shot_id, @player_id, @shot_type, @speed, @spin, @outcome, @from_zone, @to_zone)
    """
    
    # 2. If part of a continuous rally, link with a ShotTransition Edge
    if data.get("prev_shot_id"):
        transition_id = f"trans_{int(time.time()*1000)}"
        insert_trans_sql = """
            INSERT INTO ShotTransitions (transition_id, from_shot_id, to_shot_id, rally_depth, pressure_level)
            VALUES (@trans_id, @from_shot, @to_shot, @depth, @pressure)
        """
        # Executed within a single, unified Spanner Omni transaction...

Spanner'ın yerel ISO GQL grafik sorgulama motorundan yararlanan bir koç veya analiz programı, hantal veritabanı birleştirmeleri çalıştırmadan karmaşık, derin atlamalı yapısal kalıpları anında sorgulayabilir:

-- Find highest-frequency transition sequences leading to player unforced errors
GRAPH PickleballTelemetry
MATCH (s1:Shots)-[t:ShotTransitions]->(s2:Shots)
WHERE s1.shot_type = 'Speedup' AND s2.outcome = 'FAULT_NET'
RETURN s1.speed_mph, s2.from_zone_id, COUNT(*) AS occurrences
ORDER BY occurrences DESC
LIMIT 5;

7. Gemini AI Taktiksel Koçluk ve Temellendirilmiş RAG Sohbeti

Şimdi uygulamamızın beynini etkinleştireceğiz: Google Gemini 3.8 Flash. Python arka ucumuz, doğrudan Spanner Omni tarafından desteklenen üç gerçek zamanlı yapay zeka özelliği sunmak için modeli kullanır.

Sistem, bazı farklı anlık yapay zeka iş akışları için Google Gemini 3.8 Flash'ı kullanır:

Gerçek Zamanlı Ralli Dökümü ve Taktik Hakem:

Her sayıdan sonra Gemini; vuruş zincirini, bot zorluğunu ve korttaki konumlandırmayı analiz eder.

Spanner Multi-Table Grounded AI Concierge & Match Analyst:

Kullanıcılar, doğal dilde kort programlarını ve tesis ücretlerini sorgulayabilir ve "OmniBot'a karşı nasıl oynadım?" diye sorabilir. Arka uç, Gemini'ı sıfır halüsinasyonla desteklemek için dört alanda (Courts, Bookings, GameSessions ve Shots) Spanner Omni'yi dinamik olarak sorgular.

Real-Time Performance Analytics API (/api/performance):

Oyuncu kontrol paneli için doğrudan Spanner Omni'nin GameSessions tablosundan canlı galibiyet-mağlubiyet oranlarını, ortalama ralli derinliğini ve son oyun oturumlarını birleştirir.

Şu anda herhangi bir işlem yapmanız gerekmiyor, yalnızca ayrıntıları inceleyin.

8. Yapılandırma, çalıştırma ve test etme

Sistemi uçtan uca doğrulamak için uygulamanın tamamını yerel makinenizde başlatalım.

  1. Ana makine terminalinizi açın ve proje deposunu klonlayın:
git clone https://github.com/AbiramiSukumaran/pickleball

cd pickleball
  1. Gemini API anahtarınızı almak için aşağıdaki belgede yer alan adımları uygulayın:

https://medium.com/google-cloud/get-api-key-from-google-ai-studio-b2d2059a171d

  1. Editörünüzde .env dosyasını açın ve ortam ayrıntılarınızı yapılandırın:
SPANNER_EMULATOR_HOST="localhost:9010"
SPANNER_PROJECT="PROJECT_ID"
SPANNER_INSTANCE="omni-instance"
SPANNER_DATABASE="pickleball-db"
GEMINI_API_KEY="REPLACE_WITH_YOUR_ACTUAL_GEMINI_KEY"
  1. Python bağımlılıklarını yükleyin ve uygulamayı çalıştırın:
pip install -r requirements.txt

python app.py
  1. Web tarayıcınızı açıp http://localhost:5000 adresine giderek oynamaya başlayın. Rezervasyon sekmesinde kort saati rezerve edebilir, ayarlanabilir DUPR OmniBot'a karşı oynayabilir ve kort kenarında anlık Gemini Maç Analisti'ne danışabilirsiniz.

Web tarayıcısını açın veya yerel ana makinenizde çalışan uygulamanın bağlantısını tıklayın.

a2d18f8288607c66.png

9. Tebrikler!

Bu codelab'i tamamlayarak veritabanı mühendisliğinde bir paradigma değişimine tanık oldunuz. Yıllar boyunca, uç noktalarda (fabrika katları, askeri bölgeler veya saha kenarı spor ağları) duyarlı ve gerçek zamanlı aracılar oluşturmak, tasarım konusunda zorlu ödünler vermeyi gerektiriyordu. İzole edilmiş işlem sistemleri, ayrı grafik düğümleri ve vektör arama dizinleri arasında hassas ETL ardışık düzenlerini bir araya getirmek gerekiyordu.

Spanner Omni bu ödün verme zorunluluğunu ortadan kaldırır. Dünya ölçeğinde dağıtılmış tutarlılığı, ISO GQL grafik sorgulamayı ve ilişkisel performansı tek bir indirilebilir yerel container'da birleştirir. Alanınız ne olursa olsun Spanner Omni, uç temsilcilerinizin sarsılmaz bir gerçek doğruya sahip olmasını garanti eder.