ক্লাউড স্প্যানার: Go দিয়ে একটি গেমিং লিডারবোর্ড তৈরি করুন

১. সংক্ষিপ্ত বিবরণ

গুগল ক্লাউড স্প্যানার হলো একটি সম্পূর্ণভাবে পরিচালিত, হরাইজন্টালি স্কেলেবল, বিশ্বব্যাপী বিতরণযোগ্য রিলেশনাল ডাটাবেস পরিষেবা, যা পারফরম্যান্স ও উচ্চ প্রাপ্যতা বজায় রেখে ACID ট্রানজ্যাকশন এবং SQL সিম্যান্টিকস প্রদান করে।

এই ল্যাবে, আপনি একটি ক্লাউড স্প্যানার ইনস্ট্যান্স সেটআপ করার পদ্ধতি শিখবেন। আপনি একটি গেমিং লিডারবোর্ডের জন্য ব্যবহারযোগ্য ডাটাবেস এবং স্কিমা তৈরির ধাপগুলো অনুসরণ করবেন। আপনি প্রথমে খেলোয়াড়দের তথ্য সংরক্ষণের জন্য একটি 'Players' টেবিল এবং খেলোয়াড়দের স্কোর সংরক্ষণের জন্য একটি 'Scores' টেবিল তৈরি করবেন।

এরপর আপনি নমুনা ডেটা দিয়ে টেবিলগুলো পূরণ করবেন। তারপর কয়েকটি সেরা দশটি নমুনা কোয়েরি চালিয়ে ল্যাবটি শেষ করবেন এবং সবশেষে রিসোর্স খালি করার জন্য ইনস্ট্যান্সটি ডিলিট করে দেবেন।

আপনি যা শিখবেন

  • কীভাবে একটি ক্লাউড স্প্যানার ইনস্ট্যান্স সেটআপ করবেন।
  • কিভাবে ডাটাবেস এবং টেবিল তৈরি করতে হয়।
  • কমিট টাইমস্ট্যাম্প কলাম কীভাবে ব্যবহার করবেন
  • টাইমস্ট্যাম্প সহ আপনার ক্লাউড স্প্যানার ডাটাবেস টেবিলে কীভাবে ডেটা লোড করবেন।
  • আপনার ক্লাউড স্প্যানার ডাটাবেস কীভাবে কোয়েরি করবেন।
  • আপনার ক্লাউড স্প্যানার ইনস্ট্যান্স কীভাবে ডিলিট করবেন।

আপনার যা যা লাগবে

আপনি এই টিউটোরিয়ালটি কীভাবে ব্যবহার করবেন?

শুধু পুরোটা পড়ুন এটি পড়ুন এবং অনুশীলনগুলো সম্পূর্ণ করুন।

গুগল ক্লাউড প্ল্যাটফর্মের সাথে আপনার অভিজ্ঞতাকে আপনি কীভাবে মূল্যায়ন করবেন?

শিক্ষানবিশ মধ্যবর্তী দক্ষ

২. সেটআপ এবং প্রয়োজনীয়তা

স্ব-গতিতে পরিবেশ সেটআপ

আপনার যদি আগে থেকে কোনো গুগল অ্যাকাউন্ট (জিমেইল বা গুগল অ্যাপস) না থাকে, তবে আপনাকে অবশ্যই একটি তৈরি করতে হবে। গুগল ক্লাউড প্ল্যাটফর্ম কনসোলে ( console.cloud.google.com ) সাইন-ইন করুন এবং একটি নতুন প্রজেক্ট তৈরি করুন।

আপনার যদি আগে থেকেই কোনো প্রজেক্ট থাকে, তাহলে কনসোলের উপরের বাম দিকের প্রজেক্ট সিলেকশন পুল-ডাউন মেনুতে ক্লিক করুন:

6c9406d9b014760.png

এবং একটি নতুন প্রজেক্ট তৈরি করতে, প্রাপ্ত ডায়ালগ বক্সে থাকা 'NEW PROJECT' বোতামটিতে ক্লিক করুন:

f708315ae07353d0.png

আপনার যদি আগে থেকে কোনো প্রজেক্ট না থাকে, তাহলে আপনার প্রথম প্রজেক্টটি তৈরি করার জন্য এইরকম একটি ডায়ালগ বক্স দেখতে পাবেন:

870a3cbd6541ee86.png

পরবর্তী প্রজেক্ট তৈরির ডায়ালগ বক্সে আপনি আপনার নতুন প্রজেক্টের বিবরণ লিখতে পারবেন:

6a92c57d3250a4b3.png

প্রজেক্ট আইডিটি মনে রাখবেন, যা সমস্ত গুগল ক্লাউড প্রজেক্ট জুড়ে একটি অনন্য নাম (উপরের নামটি ইতিমধ্যে ব্যবহৃত হয়েছে এবং আপনার জন্য কাজ করবে না, দুঃখিত!)। এই কোডল্যাবে এটিকে পরবর্তীতে PROJECT_ID হিসাবে উল্লেখ করা হবে।

এরপরে, যদি আপনি আগে থেকে তা না করে থাকেন, তাহলে Google Cloud রিসোর্স ব্যবহার করতে এবং Cloud Spanner API সক্রিয় করতে আপনাকে ডেভেলপার কনসোলে বিলিং চালু করতে হবে।

15d0ef27a8fbab27.png

এই কোডল্যাবটি চালাতে আপনার কয়েক ডলারের বেশি খরচ হওয়ার কথা নয়, কিন্তু আপনি যদি আরও রিসোর্স ব্যবহার করার সিদ্ধান্ত নেন অথবা সেগুলোকে চালু রাখেন, তাহলে খরচ আরও বেশি হতে পারে (এই ডকুমেন্টের শেষে 'ক্লিনআপ' অংশটি দেখুন)। গুগল ক্লাউড স্প্যানারের মূল্য তালিকা এখানে নথিভুক্ত করা আছে।

গুগল ক্লাউড প্ল্যাটফর্মের নতুন ব্যবহারকারীরা ৩০০ ডলারের একটি ফ্রি ট্রায়ালের জন্য যোগ্য, যার ফলে এই কোডল্যাবটি সম্পূর্ণ বিনামূল্যে পাওয়া যাবে।

গুগল ক্লাউড শেল সেটআপ

যদিও গুগল ক্লাউড এবং স্প্যানার আপনার ল্যাপটপ থেকে দূরবর্তীভাবে পরিচালনা করা যায়, এই কোডল্যাবে আমরা গুগল ক্লাউড শেল ব্যবহার করব, যা ক্লাউডে চালিত একটি কমান্ড লাইন পরিবেশ।

এই ডেবিয়ান-ভিত্তিক ভার্চুয়াল মেশিনটিতে আপনার প্রয়োজনীয় সমস্ত ডেভেলপমেন্ট টুলস লোড করা আছে। এটি একটি স্থায়ী ৫ জিবি হোম ডিরেক্টরি প্রদান করে এবং গুগল ক্লাউডে চলে, যা নেটওয়ার্ক পারফরম্যান্স ও অথেনটিকেশনকে ব্যাপকভাবে উন্নত করে। এর মানে হলো, এই কোডল্যাবের জন্য আপনার শুধু একটি ব্রাউজার প্রয়োজন হবে (হ্যাঁ, এটি ক্রোমবুকেও কাজ করে)।

  1. ক্লাউড কনসোল থেকে ক্লাউড শেল সক্রিয় করতে, কেবল 'Activate Cloud Shell'-এ ক্লিক করুন। a8460e837e9f5fda.png (পরিবেশের জন্য ব্যবস্থা করতে এবং সংযোগ স্থাপন করতে মাত্র কয়েক মুহূর্ত সময় লাগা উচিত)।

b532b2f19ab85dda.png

Screen Shot 2017-06-14 at 10.13.43 PM.png

ক্লাউড শেলে সংযুক্ত হওয়ার পর, আপনি দেখতে পাবেন যে আপনাকে ইতিমধ্যেই প্রমাণীকৃত করা হয়েছে এবং প্রজেক্টটি আপনার PROJECT_ID তে সেট করা আছে।

gcloud auth list

কমান্ড আউটপুট

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

কমান্ড আউটপুট

[core]
project = <PROJECT_ID>

যদি কোনো কারণে প্রজেক্টটি সেট করা না থাকে, তাহলে নিম্নলিখিত কমান্ডটি দিন:

gcloud config set project <PROJECT_ID>

আপনার PROJECT_ID খুঁজছেন? সেটআপের ধাপগুলিতে আপনি কোন আইডি ব্যবহার করেছিলেন তা দেখে নিন অথবা ক্লাউড কনসোল ড্যাশবোর্ডে এটি খুঁজে দেখুন:

2485e00c1223af09.png

ক্লাউড শেল ডিফল্টরূপে কিছু এনভায়রনমেন্ট ভেরিয়েবলও সেট করে, যা ভবিষ্যতে কমান্ড চালানোর সময় কাজে লাগতে পারে।

echo $GOOGLE_CLOUD_PROJECT

কমান্ড আউটপুট

<PROJECT_ID>
  1. অবশেষে, ডিফল্ট জোন এবং প্রজেক্ট কনফিগারেশন সেট করুন।
gcloud config set compute/zone us-central1-f

আপনি বিভিন্ন ধরনের জোন বেছে নিতে পারেন। আরও তথ্যের জন্য, অঞ্চল ও জোন দেখুন।

সারসংক্ষেপ

এই ধাপে আপনি আপনার পরিবেশ সেটআপ করবেন।

এরপরে

এরপরে, আপনাকে একটি ক্লাউড স্প্যানার ইনস্ট্যান্স সেটআপ করতে হবে।

৩. একটি ক্লাউড স্প্যানার ইনস্ট্যান্স সেটআপ করুন

এই ধাপে আমরা এই কোডল্যাবের জন্য আমাদের ক্লাউড স্প্যানার ইনস্ট্যান্স সেটআপ করব। স্প্যানার এন্ট্রিটি অনুসন্ধান করুন। 1a6580bd3d3e6783.png বাম উপরের হ্যামবার্গার মেনুতে 3129589f7bc9e5ce.png অথবা "/" চেপে "Spanner" টাইপ করে স্প্যানার অনুসন্ধান করুন।

36e52f8df8e13b99.png

এরপর, ক্লিক করুন 19bb9864067757cb.png এবং আপনার ইনস্ট্যান্সের জন্য ইনস্ট্যান্সের নাম ‘cloudspanner-leaderboard’ লিখে, একটি কনফিগারেশন বেছে নিয়ে (একটি রিজিওনাল ইনস্ট্যান্স নির্বাচন করুন), এবং নোডের সংখ্যা নির্ধারণ করে ফর্মটি পূরণ করুন; এই কোডল্যাবের জন্য আমাদের কেবল ১টি নোড প্রয়োজন হবে। প্রোডাকশন ইনস্ট্যান্সের জন্য এবং ক্লাউড স্প্যানার SLA-এর যোগ্যতা অর্জনের জন্য আপনাকে আপনার ক্লাউড স্প্যানার ইনস্ট্যান্সে ৩ বা তার বেশি নোড চালাতে হবে।

সবশেষে, 'Create'-এ ক্লিক করুন এবং কয়েক সেকেন্ডের মধ্যেই আপনার ব্যবহারের জন্য একটি ক্লাউড স্প্যানার ইনস্ট্যান্স পেয়ে যাবেন।

dceb68e9ed3801e8.png

পরবর্তী ধাপে আমরা আমাদের নতুন ইনস্ট্যান্সটিতে একটি ডাটাবেস এবং স্কিমা তৈরি করার জন্য গো ক্লায়েন্ট লাইব্রেরি ব্যবহার করব।

৪. একটি ডাটাবেস এবং স্কিমা তৈরি করুন

এই ধাপে আমরা আমাদের নমুনা ডাটাবেস এবং স্কিমা তৈরি করব।

চলুন, গো ক্লায়েন্ট লাইব্রেরি ব্যবহার করে দুটি টেবিল তৈরি করি; খেলোয়াড়ের তথ্যের জন্য একটি Players টেবিল এবং খেলোয়াড়ের স্কোর সংরক্ষণের জন্য একটি Scores টেবিল। এটি করার জন্য, আমরা ক্লাউড শেলে একটি গো কনসোল অ্যাপ্লিকেশন তৈরির ধাপগুলো অনুসরণ করব।

প্রথমে ক্লাউড শেলে নিম্নলিখিত কমান্ডটি টাইপ করে গিটহাব থেকে এই কোডল্যাবের নমুনা কোডটি ক্লোন করুন:

export GO111MODULE=auto
go get -u github.com/GoogleCloudPlatform/golang-samples/spanner/...

এরপর 'লিডারবোর্ড' ডিরেক্টরিতে যান, যেখানে আপনি আপনার অ্যাপ্লিকেশনটি তৈরি করবেন।

cd gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/spanner_leaderboard

এই কোডল্যাবের জন্য প্রয়োজনীয় সমস্ত কোড বিদ্যমান golang-samples/spanner/spanner_leaderboard/ ডিরেক্টরিতে leaderboard নামের একটি রানযোগ্য Go অ্যাপ্লিকেশন হিসেবে রয়েছে, যা কোডল্যাবটি করার সময় আপনার রেফারেন্স হিসেবে কাজ করবে। আমরা পর্যায়ক্রমে একটি নতুন ডিরেক্টরি তৈরি করব এবং লিডারবোর্ড অ্যাপ্লিকেশনটির একটি কপি বিল্ড করব।

অ্যাপ্লিকেশনটির জন্য 'codelab' নামে একটি নতুন ডিরেক্টরি তৈরি করুন এবং নিম্নলিখিত কমান্ডটি ব্যবহার করে সেটিতে প্রবেশ করুন:

mkdir codelab && cd $_

এখন চলুন 'লিডারবোর্ড' নামে একটি সাধারণ গো অ্যাপ্লিকেশন তৈরি করি, যা স্প্যানার ক্লায়েন্ট লাইব্রেরি ব্যবহার করে প্লেয়ার্স এবং স্কোর্স—এই দুটি টেবিল নিয়ে একটি লিডারবোর্ড তৈরি করবে। আপনি এই কাজটি সরাসরি ক্লাউড শেল এডিটরেই করতে পারেন:

নীচে হাইলাইট করা 'ওপেন এডিটর' আইকনে ক্লিক করে ক্লাউড শেল এডিটর খুলুন:

7519d016b96ca51b.png

~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/spanner/codelab ফোল্ডারে 'leaderboard.go' নামে একটি ফাইল তৈরি করুন।

  • প্রথমে নিশ্চিত হয়ে নিন যে ক্লাউড শেল এডিটরের ফোল্ডার তালিকায় আপনি 'codelab' ফোল্ডারটি নির্বাচন করেছেন।
  • এরপর ক্লাউড শেল এডিটরের 'ফাইল' মেনুর অধীনে 'নতুন ফাইল' নির্বাচন করুন।
  • নতুন ফাইলটির নাম হিসেবে 'leaderboard.go' লিখুন।

এটি অ্যাপ্লিকেশনটির মূল ফাইল, যেখানে আমাদের অ্যাপ্লিকেশন কোড এবং যেকোনো নির্ভরতা অন্তর্ভুক্ত করার জন্য রেফারেন্স থাকবে।

leaderboard ডেটাবেস এবং PlayersScores টেবিল তৈরি করতে, নিচের Go কোডটি কপি (Ctrl + P) করে leaderboard.go ফাইলে পেস্ট (Ctrl + V) করুন:

package main

import (
        "context"
        "flag"
        "fmt"
        "io"
        "log"
        "os"
        "regexp"
        "time"

        "cloud.google.com/go/spanner"
        database "cloud.google.com/go/spanner/admin/database/apiv1"

        adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

type adminCommand func(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, database string) error


func createDatabase(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, db string) error {
        matches := regexp.MustCompile("^(.*)/databases/(.*)$").FindStringSubmatch(db)
        if matches == nil || len(matches) != 3 {
                return fmt.Errorf("Invalid database id %s", db)
        }
        op, err := adminClient.CreateDatabase(ctx, &adminpb.CreateDatabaseRequest{
                Parent:          matches[1],
                CreateStatement: "CREATE DATABASE `" + matches[2] + "`",
                ExtraStatements: []string{
                        `CREATE TABLE Players(
                            PlayerId INT64 NOT NULL,
                            PlayerName STRING(2048) NOT NULL
                        ) PRIMARY KEY(PlayerId)`,
                        `CREATE TABLE Scores(
                            PlayerId INT64 NOT NULL,
                            Score INT64 NOT NULL,
                            Timestamp TIMESTAMP NOT NULL
                            OPTIONS(allow_commit_timestamp=true)
                        ) PRIMARY KEY(PlayerId, Timestamp),
                        INTERLEAVE IN PARENT Players ON DELETE NO ACTION`,
                },
        })
        if err != nil {
                return err
        }
        if _, err := op.Wait(ctx); err != nil {
                return err
        }
        fmt.Fprintf(w, "Created database [%s]\n", db)
        return nil
}

func createClients(ctx context.Context, db string) (*database.DatabaseAdminClient, *spanner.Client) {
        adminClient, err := database.NewDatabaseAdminClient(ctx)
        if err != nil {
                log.Fatal(err)
        }

        dataClient, err := spanner.NewClient(ctx, db)
        if err != nil {
                log.Fatal(err)
        }

        return adminClient, dataClient
}

func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
        cmd string, db string, timespan int) error {
        // createdatabase command
        if cmd == "createdatabase" {
                err := createDatabase(ctx, w, adminClient, db)
                if err != nil {
                        fmt.Fprintf(w, "%s failed with %v", cmd, err)
                }
                return err
        }
        return nil
}

func main() {
        flag.Usage = func() {
                fmt.Fprintf(os.Stderr, `Usage: leaderboard <command> <database_name> [command_option]

        Command can be one of: createdatabase

Examples:
        leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
                - Create a sample Cloud Spanner database along with sample tables in your project.
`)
        }

        flag.Parse()
        flagCount := len(flag.Args())
        if flagCount != 2 {
                flag.Usage()
                os.Exit(2)
        }

        cmd, db := flag.Arg(0), flag.Arg(1)
        // Set timespan to zero, as it's not currently being used
        var timespan int = 0

        ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
        defer cancel()
        adminClient, dataClient := createClients(ctx, db)
        if err := run(ctx, adminClient, dataClient, os.Stdout, cmd, db, timespan); err != nil {
                os.Exit(1)
        }
}

Cloud Shell Editor-এর 'File' মেনুর অধীনে 'Save' নির্বাচন করে leaderboard.go ফাইলে আপনার করা পরিবর্তনগুলি সংরক্ষণ করুন।

` createdatabase কমান্ডটি সক্রিয় করার জন্য কোড যোগ করার পর আপনার ` leaderboard.go ফাইলটি কেমন দেখতে হবে, তার একটি উদাহরণ দেখতে আপনি golang-samples/spanner/spanner_leaderboard ডিরেক্টরিতে থাকা ` leaderboard.go ফাইলটি ব্যবহার করতে পারেন।

ক্লাউড শেলে আপনার অ্যাপটি বিল্ড করতে, codelab ডিরেক্টরি থেকে 'go build' কমান্ডটি চালান, যেখানে আপনার leaderboard.go ফাইলটি অবস্থিত:

go build leaderboard.go

আপনার অ্যাপ্লিকেশনটি সফলভাবে বিল্ড হয়ে গেলে, ক্লাউড শেলে নিম্নলিখিত কমান্ডটি প্রবেশ করিয়ে অ্যাপ্লিকেশনটি চালান:

./leaderboard

আপনি নিম্নলিখিতের মতো আউটপুট দেখতে পাবেন:

Usage: leaderboard <command> <database_name> [command_option]

        Command can be one of: createdatabase

Examples:
        leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
                - Create a sample Cloud Spanner database along with sample tables in your project.

এই প্রতিক্রিয়া থেকে আমরা দেখতে পাচ্ছি যে এটি Leaderboard অ্যাপ্লিকেশন, যেটিতে বর্তমানে একটিমাত্র সম্ভাব্য কমান্ড রয়েছে: createdatabase । আমরা দেখতে পাচ্ছি যে createdatabase কমান্ডের প্রত্যাশিত আর্গুমেন্টটি হলো একটি স্ট্রিং, যাতে একটি নির্দিষ্ট ইনস্ট্যান্স আইডি এবং ডেটাবেস আইডি থাকে।

এখন নিচের কমান্ডটি চালান। খেয়াল রাখবেন, my-project জায়গায় এই কোডল্যাবের শুরুতে আপনার তৈরি করা প্রজেক্ট আইডিটি বসাবেন।

./leaderboard createdatabase projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard

কয়েক সেকেন্ড পরে আপনি নিম্নলিখিতের মতো একটি প্রতিক্রিয়া দেখতে পাবেন:

Created database [projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard] 

ক্লাউড কনসোলের ক্লাউড স্প্যানার ডেটাবেস ওভারভিউ বিভাগে বাম দিকের মেনুতে আপনার নতুন ডেটাবেস এবং টেবিলগুলো দেখতে পাবেন।

a12fa65e352836b1.png

পরবর্তী ধাপে আমরা আপনার নতুন ডেটাবেসে কিছু ডেটা লোড করার জন্য আমাদের অ্যাপ্লিকেশনটি আপডেট করব।

৫. ডেটা লোড করুন

এখন আমাদের কাছে leaderboard নামে একটি ডাটাবেস আছে, যাতে Players এবং Scores নামে দুটি টেবিল রয়েছে। এখন চলুন গো ক্লায়েন্ট লাইব্রেরি ব্যবহার করে আমাদের Players টেবিলে খেলোয়াড়দের তথ্য এবং Scores টেবিলে প্রত্যেক খেলোয়াড়ের জন্য র‍্যান্ডম স্কোর যুক্ত করি।

যদি আগে থেকে খোলা না থাকে, তাহলে নিচে হাইলাইট করা আইকনটিতে ক্লিক করে ক্লাউড শেল এডিটরটি খুলুন:

7519d016b96ca51b.png

এরপর, ক্লাউড শেল এডিটরে leaderboard.go ফাইলটি এডিট করে একটি insertplayers কমান্ড যোগ করুন, যা Players টেবিলে ১০০ জন প্লেয়ার যুক্ত করতে ব্যবহার করা যাবে। এছাড়াও আমরা একটি insertscores কমান্ড যোগ করব, যা Players টেবিলের প্রত্যেক প্লেয়ারের জন্য Scores টেবিলে এলোমেলোভাবে ৪টি স্কোর যুক্ত করতে ব্যবহার করা যাবে।

প্রথমে leaderboard.go ফাইলের উপরের দিকে থাকা imports সেকশনটি আপডেট করুন এবং সেখানে বর্তমানে যা আছে তা প্রতিস্থাপন করুন, যাতে কাজ শেষ হলে এটি দেখতে নিচের মতো হয়:

import (
        "context"
        "flag"
        "fmt"
        "io"
        "log"
        "math/rand"
        "os"
        "regexp"
        "time"

        "cloud.google.com/go/spanner"
        database "cloud.google.com/go/spanner/admin/database/apiv1"

        "google.golang.org/api/iterator"
        adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

এরপর ফাইলের একদম উপরে, "type adminCommand ..." দিয়ে শুরু হওয়া লাইনটির ঠিক নিচে, একটি নতুন কমান্ড টাইপ এবং কমান্ডগুলোর একটি তালিকা যোগ করুন, যাতে কাজটি শেষ হলে ফাইলটি দেখতে নিচের মতো হয়:

type adminCommand func(ctx context.Context, w io.Writer, adminClient *database.DatabaseAdminClient, database string) error

type command func(ctx context.Context, w io.Writer, client *spanner.Client) error
var (
        commands = map[string]command{
                "insertplayers": insertPlayers,
                "insertscores":  insertScores,
        }
)

এরপরে বিদ্যমান createdatabase() ফাংশনের নিচে insertPlayers এবং insertScores ফাংশনগুলো যোগ করুন:

func insertPlayers(ctx context.Context, w io.Writer, client *spanner.Client) error {
        // Get number of players to use as an incrementing value for each PlayerName to be inserted
        stmt := spanner.Statement{
                SQL: `SELECT Count(PlayerId) as PlayerCount FROM Players`,
        }
        iter := client.Single().Query(ctx, stmt)
        defer iter.Stop()
        row, err := iter.Next()
        if err != nil {
                return err
        }
        var numberOfPlayers int64 = 0
        if err := row.Columns(&numberOfPlayers); err != nil {
                return err
        }
        // Initialize values for random PlayerId
        rand.Seed(time.Now().UnixNano())
        min := 1000000000
        max := 9000000000
        // Insert 100 player records into the Players table
        _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
                stmts := []spanner.Statement{}
                for i := 1; i <= 100; i++ {
                        numberOfPlayers++
                        playerID := rand.Intn(max-min) + min
                        playerName := fmt.Sprintf("Player %d", numberOfPlayers)
                        stmts = append(stmts, spanner.Statement{
                                SQL: `INSERT INTO Players
                                                (PlayerId, PlayerName)
                                                VALUES (@playerID, @playerName)`,
                                Params: map[string]interface{}{
                                        "playerID":   playerID,
                                        "playerName": playerName,
                                },
                        })
                }
                _, err := txn.BatchUpdate(ctx, stmts)
                if err != nil {
                        return err
                }
                return nil
        })
        fmt.Fprintf(w, "Inserted players \n")
        return nil
}

func insertScores(ctx context.Context, w io.Writer, client *spanner.Client) error {
        playerRecordsFound := false
        // Create slice for insert statements
        stmts := []spanner.Statement{}
        // Select all player records
        stmt := spanner.Statement{SQL: `SELECT PlayerId FROM Players`}
        iter := client.Single().Query(ctx, stmt)
        defer iter.Stop()
        // Insert 4 score records into the Scores table for each player in the Players table
        for {
                row, err := iter.Next()
                if err == iterator.Done {
                        break
                }
                if err != nil {
                        return err
                }
                playerRecordsFound = true
                var playerID int64
                if err := row.ColumnByName("PlayerId", &playerID); err != nil {
                        return err
                }
                // Initialize values for random score and date
                rand.Seed(time.Now().UnixNano())
                min := 1000
                max := 1000000
                for i := 0; i < 4; i++ {
                        // Generate random score between 1,000 and 1,000,000
                        score := rand.Intn(max-min) + min
                        // Generate random day within the past two years
                        now := time.Now()
                        endDate := now.Unix()
                        past := now.AddDate(0, -24, 0)
                        startDate := past.Unix()
                        randomDateInSeconds := rand.Int63n(endDate-startDate) + startDate
                        randomDate := time.Unix(randomDateInSeconds, 0)
                        // Add insert statement to stmts slice
                        stmts = append(stmts, spanner.Statement{
                                SQL: `INSERT INTO Scores
                                                (PlayerId, Score, Timestamp)
                                                VALUES (@playerID, @score, @timestamp)`,
                                Params: map[string]interface{}{
                                        "playerID":  playerID,
                                        "score":     score,
                                        "timestamp": randomDate,
                                },
                        })
                }

        }
        if !playerRecordsFound {
                fmt.Fprintln(w, "No player records currently exist. First insert players then insert scores.")
        } else {
                _, err := client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
                        // Commit insert statements for all scores to be inserted as a single transaction
                        _, err := txn.BatchUpdate(ctx, stmts)
                        return err
                })
                if err != nil {
                        return err
                }
                fmt.Fprintln(w, "Inserted scores")
        }
        return nil
}

তারপর, insert কমান্ডটি কার্যকর করতে, আপনার অ্যাপ্লিকেশনের "run" ফাংশনে createdatabase হ্যান্ডলিং স্টেটমেন্টের নিচে return nil স্টেটমেন্টটি প্রতিস্থাপন করে নিম্নলিখিত কোডটি যোগ করুন:

        // insert and query commands
        cmdFn := commands[cmd]
        if cmdFn == nil {
                flag.Usage()
                os.Exit(2)
        }
        err := cmdFn(ctx, w, dataClient)
        if err != nil {
                fmt.Fprintf(w, "%s failed with %v", cmd, err)
        }
        return err

আপনার কাজ শেষ হলে run ফাংশনটি দেখতে নিচের মতো হবে:

func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
        cmd string, db string, timespan int) error {
        // createdatabase command
        if cmd == "createdatabase" {
                err := createDatabase(ctx, w, adminClient, db)
                if err != nil {
                        fmt.Fprintf(w, "%s failed with %v", cmd, err)
                }
                return err
        }

        // insert and query commands
        cmdFn := commands[cmd]
        if cmdFn == nil {
                flag.Usage()
                os.Exit(2)
        }
        err := cmdFn(ctx, w, dataClient)
        if err != nil {
                fmt.Fprintf(w, "%s failed with %v", cmd, err)
        }
        return err
}

আপনার অ্যাপ্লিকেশনে "insert" কার্যকারিতা যোগ করার চূড়ান্ত ধাপ হলো flag.Usage() ফাংশনে "insertplayers" এবং "insertscores" কমান্ডগুলোর জন্য সাহায্যমূলক লেখা (help text) যোগ করা। insert কমান্ডগুলোর জন্য সাহায্যমূলক লেখা অন্তর্ভুক্ত করতে flag.Usage() ফাংশনে নিম্নলিখিত সাহায্যমূলক লেখাটি যোগ করুন:

সম্ভাব্য কমান্ডের তালিকায় কমান্ড দুটি যোগ করুন:

Command can be one of: createdatabase, insertplayers, insertscores

এবং createdatabase কমান্ডের হেল্প টেক্সটের নিচে এই অতিরিক্ত হেল্প টেক্সটটি যোগ করুন।

        leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
                - Insert 100 sample Player records into the database.
        leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
                - Insert sample score data into Scores sample Cloud Spanner database table.

Cloud Shell Editor-এর 'File' মেনুর অধীনে 'Save' নির্বাচন করে leaderboard.go ফাইলে আপনার করা পরিবর্তনগুলি সংরক্ষণ করুন।

insertplayers এবং insertscores কমান্ডগুলো সক্রিয় করার কোড যোগ করার পর আপনার leaderboard.go ফাইলটি কেমন দেখতে হবে, তার একটি উদাহরণ দেখতে আপনি golang-samples/spanner/spanner_leaderboard ডিরেক্টরিতে থাকা leaderboard.go ফাইলটি ব্যবহার করতে পারেন।

এখন অ্যাপ্লিকেশনটি বিল্ড এবং রান করে নিশ্চিত করা যাক যে নতুন insertplayers এবং insertscores কমান্ডগুলো অ্যাপ্লিকেশনটির সম্ভাব্য কমান্ডের তালিকায় অন্তর্ভুক্ত হয়েছে। অ্যাপ্লিকেশনটি বিল্ড করতে নিম্নলিখিত কমান্ডটি চালান:

go build leaderboard.go

নিম্নলিখিত কমান্ডটি প্রবেশ করিয়ে ক্লাউড শেলে প্রাপ্ত অ্যাপ্লিকেশনটি চালান:

./leaderboard

অ্যাপ্লিকেশনটির ডিফল্ট আউটপুটে এখন insertplayers এবং insertscores কমান্ডগুলো অন্তর্ভুক্ত দেখতে পাবেন:

Usage: leaderboard <command> <database_name> [command_option]

        Command can be one of: createdatabase, insertplayers, insertscores

Examples:
        leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
                - Create a sample Cloud Spanner database along with sample tables in your project.
        leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
                - Insert 100 sample Player records into the database.
        leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
                - Insert sample score data into Scores sample Cloud Spanner database table.

এবার আমরা ` createdatabase কমান্ড কল করার সময় যে আর্গুমেন্ট ভ্যালুগুলো ব্যবহার করেছিলাম, সেগুলো দিয়েই ` insertplayers কমান্ডটি রান করব। খেয়াল রাখবেন, my-project জায়গায় এই কোডল্যাবের শুরুতে আপনার তৈরি করা প্রজেক্ট আইডিটি বসাবেন।

./leaderboard insertplayers projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard

কয়েক সেকেন্ড পরে আপনি নিম্নলিখিতের মতো একটি প্রতিক্রিয়া দেখতে পাবেন:

Inserted players

এখন চলুন Go ক্লায়েন্ট লাইব্রেরি ব্যবহার করে আমাদের Scores টেবিলটি পূরণ করি, যেখানে Players টেবিলের প্রতিটি খেলোয়াড়ের জন্য টাইমস্ট্যাম্পসহ চারটি র‍্যান্ডম স্কোর থাকবে।

পূর্বে create কমান্ডটি চালানোর সময় কার্যকর করা নিম্নলিখিত SQL স্টেটমেন্টটির মাধ্যমে Scores টেবিলের Timestamp কলামটিকে একটি "commit timestamp" কলাম হিসেবে সংজ্ঞায়িত করা হয়েছিল:

CREATE TABLE Scores(
  PlayerId INT64 NOT NULL,
  Score INT64 NOT NULL,
  Timestamp TIMESTAMP NOT NULL OPTIONS(allow_commit_timestamp=true)
) PRIMARY KEY(PlayerId, Timestamp),
    INTERLEAVE IN PARENT Players ON DELETE NO ACTION

OPTIONS(allow_commit_timestamp=true) অ্যাট্রিবিউটটি লক্ষ্য করুন। এটি Timestamp একটি "কমিট টাইমস্ট্যাম্প" কলামে পরিণত করে এবং একটি নির্দিষ্ট টেবিল সারিতে INSERT ও UPDATE অপারেশনের জন্য এটিকে সঠিক ট্রানজ্যাকশন টাইমস্ট্যাম্প দিয়ে স্বয়ংক্রিয়ভাবে পূরণ করতে সক্ষম করে।

আপনি 'কমিট টাইমস্ট্যাম্প' কলামে আপনার নিজের টাইমস্ট্যাম্প মানও যোগ করতে পারেন, তবে শর্ত হলো আপনাকে এমন একটি টাইমস্ট্যাম্প যোগ করতে হবে যার মান অতীত, এবং এই কোডল্যাবের উদ্দেশ্যে আমরা ঠিক তাই করব।

এখন, insertplayers কমান্ড কল করার সময় আমরা যে আর্গুমেন্ট ভ্যালুগুলো ব্যবহার করেছিলাম, সেগুলো দিয়েই insertscores কমান্ডটি রান করি। খেয়াল রাখবেন, my-project জায়গায় এই কোডল্যাবের শুরুতে আপনার তৈরি করা প্রজেক্ট আইডিটি বসাবেন।

./leaderboard insertscores projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard

কয়েক সেকেন্ড পরে আপনি নিম্নলিখিতের মতো একটি প্রতিক্রিয়া দেখতে পাবেন:

Inserted scores

insertScores ফাংশনটি চালালে অতীতের কোনো তারিখ ও সময় সহ এলোমেলোভাবে তৈরি একটি টাইমস্ট্যাম্প সন্নিবেশ করতে নিম্নলিখিত কোড স্নিপেটটি ব্যবহৃত হয়:

now := time.Now()
endDate := now.Unix()
past := now.AddDate(0, -24, 0)
startDate := past.Unix()
randomDateInSeconds := rand.Int63n(endDate-startDate) + startDate
randomDate := time.Unix(randomDateInSeconds, 0)
stmts = append(stmts, spanner.Statement{
        SQL: `INSERT INTO Scores
              (PlayerId, Score, Timestamp)
                 VALUES (@playerID, @score, @timestamp)`,
        Params: map[string]interface{}{
                "playerID":  playerID,
                "score":     score,
                "timestamp": randomDate,
        },
})

'Insert' ট্রানজ্যাকশনটি ঠিক কখন সংঘটিত হয়, সেই মুহূর্তের টাইমস্ট্যাম্প দিয়ে Timestamp কলামটি স্বয়ংক্রিয়ভাবে পূরণ করতে, আপনি এর পরিবর্তে নিম্নলিখিত কোড স্নিপেটের মতো Go কনস্ট্যান্ট spanner.CommitTimestamp ইনসার্ট করতে পারেন:

...
stmts = append(stmts, spanner.Statement{
        SQL: `INSERT INTO Scores
              (PlayerId, Score, Timestamp)
                 VALUES (@playerID, @score, @timestamp)`,
        Params: map[string]interface{}{
                "playerID":  playerID,
                "score":     score,
                "timestamp": spanner.CommitTimestamp,
        },
})

এখন যেহেতু আমরা ডেটা লোড করা সম্পন্ন করেছি, চলুন ক্লাউড কনসোলের ক্লাউড স্প্যানার বিভাগে আমাদের নতুন টেবিলগুলিতে লেখা মানগুলি যাচাই করে নিই। প্রথমে leaderboard ডাটাবেসটি নির্বাচন করুন এবং তারপরে Players টেবিলটি নির্বাচন করুন। Data ট্যাবে ক্লিক করুন। আপনি দেখতে পাবেন যে টেবিলের PlayerId এবং PlayerName কলামগুলিতে ডেটা রয়েছে।

86dc5b927809a4ec.png

এরপর, Scores টেবিলে ক্লিক করে Data ট্যাবটি নির্বাচন করে যাচাই করে নেওয়া যাক যে টেবিলটিতেও ডেটা আছে কিনা। আপনি দেখতে পাবেন যে টেবিলটির PlayerId , Timestamp এবং Score কলামগুলিতে ডেটা রয়েছে।

87c8610c92d3c612.png

খুব ভালো! চলো আমাদের অ্যাপটি আপডেট করি, যাতে এটি এমন কিছু কোয়েরি চালাতে পারে যা ব্যবহার করে আমরা একটি গেমিং লিডারবোর্ড তৈরি করতে পারব।

৬. লিডারবোর্ড কোয়েরি চালান

এখন যেহেতু আমরা আমাদের ডাটাবেস তৈরি করে টেবিলগুলোতে তথ্য লোড করে ফেলেছি, চলুন এই ডেটা ব্যবহার করে একটি লিডারবোর্ড তৈরি করি। এটি করার জন্য আমাদের নিম্নলিখিত চারটি প্রশ্নের উত্তর দিতে হবে:

  1. সর্বকালের সেরা দশজন খেলোয়াড় কারা?
  2. এ বছরের 'সেরা দশ' খেলোয়াড় কারা?
  3. এই মাসের 'সেরা দশ' খেলোয়াড় কারা?
  4. এই সপ্তাহের 'সেরা দশ' খেলোয়াড় কারা?

চলুন, এই প্রশ্নগুলোর উত্তর দেবে এমন SQL কোয়েরিগুলো চালানোর জন্য আমাদের অ্যাপ্লিকেশনটি আপডেট করি।

আমরা একটি query কমান্ড এবং একটি queryWithTimespan কমান্ড যোগ করব, যা আমাদের লিডারবোর্ডের জন্য প্রয়োজনীয় তথ্য তৈরি করতে প্রশ্নগুলোর উত্তর দেওয়ার জন্য কোয়েরিগুলো চালানোর একটি উপায় প্রদান করবে।

অ্যাপ্লিকেশনটি আপডেট করতে ক্লাউড শেল এডিটরে leaderboard.go ফাইলটি সম্পাদনা করুন এবং একটি query কমান্ড ও একটি queryWithTimespan কমান্ড যোগ করুন। এছাড়াও, কমা ব্যবহার করে আমাদের স্কোর ফরম্যাট করার জন্য আমরা একটি formatWithCommas হেল্পার ফাংশন যোগ করব।

প্রথমে leaderboard.go ফাইলের উপরের দিকে থাকা imports সেকশনটি আপডেট করুন এবং সেখানে বর্তমানে যা আছে তা প্রতিস্থাপন করুন, যাতে কাজ শেষ হলে এটি দেখতে নিচের মতো হয়:

import (
        "bytes"
        "context"
        "flag"
        "fmt"
        "io"
        "log"
        "math/rand"
        "os"
        "regexp"
        "strconv"
        "time"

        "cloud.google.com/go/spanner"
        database "cloud.google.com/go/spanner/admin/database/apiv1"

        "google.golang.org/api/iterator"
        adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

এরপরে বিদ্যমান insertScores মেথডের নিচে নিম্নলিখিত দুটি ফাংশন এবং হেল্পার ফাংশনটি যোগ করুন:

func query(ctx context.Context, w io.Writer, client *spanner.Client) error {
        stmt := spanner.Statement{
                SQL: `SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
                        FROM Players p
                        JOIN Scores s ON p.PlayerId = s.PlayerId
                        ORDER BY s.Score DESC LIMIT 10`}
        iter := client.Single().Query(ctx, stmt)
        defer iter.Stop()
        for {
                row, err := iter.Next()
                if err == iterator.Done {
                        return nil
                }
                if err != nil {
                        return err
                }
                var playerID, score int64
                var playerName string
                var timestamp time.Time
                if err := row.Columns(&playerID, &playerName, &score, &timestamp); err != nil {
                        return err
                }
                fmt.Fprintf(w, "PlayerId: %d  PlayerName: %s  Score: %s  Timestamp: %s\n",
                        playerID, playerName, formatWithCommas(score), timestamp.String()[0:10])
        }
}

func queryWithTimespan(ctx context.Context, w io.Writer, client *spanner.Client, timespan int) error {
        stmt := spanner.Statement{
                SQL: `SELECT p.PlayerId, p.PlayerName, s.Score, s.Timestamp
                                FROM Players p
                                JOIN Scores s ON p.PlayerId = s.PlayerId 
                                WHERE s.Timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL @Timespan HOUR)
                                ORDER BY s.Score DESC LIMIT 10`,
                Params: map[string]interface{}{"Timespan": timespan},
        }
        iter := client.Single().Query(ctx, stmt)
        defer iter.Stop()
        for {
                row, err := iter.Next()
                if err == iterator.Done {
                        return nil
                }
                if err != nil {
                        return err
                }
                var playerID, score int64
                var playerName string
                var timestamp time.Time
                if err := row.Columns(&playerID, &playerName, &score, &timestamp); err != nil {
                        return err
                }
                fmt.Fprintf(w, "PlayerId: %d  PlayerName: %s  Score: %s  Timestamp: %s\n",
                        playerID, playerName, formatWithCommas(score), timestamp.String()[0:10])
        }
}

func formatWithCommas(n int64) string {
        numberAsString := strconv.FormatInt(n, 10)
        numberLength := len(numberAsString)
        if numberLength < 4 {
                return numberAsString
        }
        var buffer bytes.Buffer
        comma := []rune(",")
        bufferPosition := numberLength % 3
        if (bufferPosition) > 0 {
                bufferPosition = 3 - bufferPosition
        }
        for i := 0; i < numberLength; i++ {
                if bufferPosition == 3 {
                        buffer.WriteRune(comma[0])
                        bufferPosition = 0
                }
                bufferPosition++
                buffer.WriteByte(numberAsString[i])
        }
        return buffer.String()
}

এরপর leaderboard.go ফাইলের একদম উপরে, " insertscores": insertScores অপশনটির ঠিক নিচে, commands ভেরিয়েবলের মধ্যে একটি কমান্ড অপশন হিসেবে 'query' যোগ করুন, যাতে commands ভেরিয়েবলটি দেখতে এইরকম হয়:

var (
        commands = map[string]command{
                "insertplayers": insertPlayers,
                "insertscores":  insertScores,
                "query":         query,
        }
)

এরপরে run ফাংশনের মধ্যে, 'createdatabase' কমান্ড সেকশনের নিচে এবং 'insert and query' কমান্ড হ্যান্ডলিং সেকশনের উপরে 'queryWithTimespan'-কে একটি কমান্ড অপশন হিসেবে যোগ করুন:

        // querywithtimespan command
        if cmd == "querywithtimespan" {
                err := queryWithTimespan(ctx, w, dataClient, timespan)
                if err != nil {
                        fmt.Fprintf(w, "%s failed with %v", cmd, err)
                }
                return err
        }

আপনার কাজ শেষ হলে run ফাংশনটি দেখতে নিচের মতো হবে:

func run(ctx context.Context, adminClient *database.DatabaseAdminClient, dataClient *spanner.Client, w io.Writer,
        cmd string, db string, timespan int) error {
        // createdatabase command
        if cmd == "createdatabase" {
                err := createDatabase(ctx, w, adminClient, db)
                if err != nil {
                        fmt.Fprintf(w, "%s failed with %v", cmd, err)
                }
                return err
        }

        // querywithtimespan command
        if cmd == "querywithtimespan" {
                if timespan == 0 {
                        flag.Usage()
                        os.Exit(2)
                }
                err := queryWithTimespan(ctx, w, dataClient, timespan)
                if err != nil {
                        fmt.Fprintf(w, "%s failed with %v", cmd, err)
                }
                return err
        }

        // insert and query commands
        cmdFn := commands[cmd]
        if cmdFn == nil {
                flag.Usage()
                os.Exit(2)
        }
        err := cmdFn(ctx, w, dataClient)
        if err != nil {
                fmt.Fprintf(w, "%s failed with %v", cmd, err)
        }
        return err
}

এরপর, queryWithTimespan কমান্ডটিকে কার্যকর করতে, আপনার অ্যাপ্লিকেশনের `"main"` মেথডের `flag.Parse()` কোড ব্লকটি আপডেট করুন, যাতে এটি দেখতে নিম্নলিখিতের মতো হয়:

        flag.Parse()
        flagCount := len(flag.Args())
        if flagCount < 2 || flagCount > 3 {
                flag.Usage()
                os.Exit(2)
        }

        cmd, db := flag.Arg(0), flag.Arg(1)
        // If query timespan flag is specified, parse to int
        var timespan int = 0
        if flagCount == 3 {
                parsedTimespan, err := strconv.Atoi(flag.Arg(2))
                if err != nil {
                        fmt.Println(err)
                        os.Exit(2)
                }
                timespan = parsedTimespan
        }

        ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
        defer cancel()
        adminClient, dataClient := createClients(ctx, db)
        if err := run(ctx, adminClient, dataClient, os.Stdout, cmd, db, timespan); err != nil {
                os.Exit(1)
        }

আপনার অ্যাপ্লিকেশনে 'query' কার্যকারিতা যোগ করার চূড়ান্ত ধাপ হলো flag.Usage() ফাংশনে 'query' এবং 'querywithtimespan' কমান্ডগুলোর জন্য সাহায্যমূলক লেখা (help text) যোগ করা। query কমান্ডগুলোর জন্য সাহায্যমূলক লেখা অন্তর্ভুক্ত করতে flag.Usage() ফাংশনে নিম্নলিখিত কোড লাইনগুলো যোগ করুন:

সম্ভাব্য কমান্ডের তালিকায় দুটি 'query' কমান্ড যোগ করুন:

Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan

এবং insertscores কমান্ডের হেল্প টেক্সটের নিচে এই অতিরিক্ত হেল্প টেক্সটটি যোগ করুন।

        leaderboard query projects/my-project/instances/my-instance/databases/example-db
                - Query players with top ten scores of all time.
        leaderboard querywithtimespan projects/my-project/instances/my-instance/databases/example-db 168
                - Query players with top ten scores within a timespan specified in hours.

Cloud Shell Editor-এর 'File' মেনুর অধীনে 'Save' নির্বাচন করে leaderboard.go ফাইলে আপনার করা পরিবর্তনগুলি সংরক্ষণ করুন।

query এবং querywithtimespan কমান্ডগুলো সক্রিয় করার কোড যোগ করার পর আপনার ` leaderboard.go ফাইলটি কেমন দেখতে হবে, তার একটি উদাহরণ দেখতে আপনি golang-samples/spanner/spanner_leaderboard ডিরেক্টরিতে থাকা ` leaderboard.go ফাইলটি ব্যবহার করতে পারেন।

এখন অ্যাপ্লিকেশনটি বিল্ড ও রান করে নিশ্চিত হওয়া যাক যে নতুন query এবং querywithtimespan কমান্ডগুলো অ্যাপ্লিকেশনটির সম্ভাব্য কমান্ডের তালিকায় অন্তর্ভুক্ত হয়েছে।

অ্যাপ্লিকেশনটি বিল্ড করতে ক্লাউড শেলে নিম্নলিখিত কমান্ডটি চালান:

go build leaderboard.go

নিম্নলিখিত কমান্ডটি প্রবেশ করিয়ে ক্লাউড শেলে প্রাপ্ত অ্যাপ্লিকেশনটি চালান:

./leaderboard

আপনি এখন অ্যাপটির ডিফল্ট আউটপুটে একটি নতুন কমান্ড অপশন হিসেবে query এবং querywithtimespan কমান্ডগুলো দেখতে পাবেন:

Usage: leaderboard <command> <database_name> [command_option]

        Command can be one of: createdatabase, insertplayers, insertscores, query, querywithtimespan

Examples:
        leaderboard createdatabase projects/my-project/instances/my-instance/databases/example-db
                - Create a sample Cloud Spanner database along with sample tables in your project.
        leaderboard insertplayers projects/my-project/instances/my-instance/databases/example-db
                - Insert 100 sample Player records into the database.
        leaderboard insertscores projects/my-project/instances/my-instance/databases/example-db
                - Insert sample score data into Scores sample Cloud Spanner database table.
        leaderboard query projects/my-project/instances/my-instance/databases/example-db
                - Query players with top ten scores of all time.
        leaderboard querywithtimespan projects/my-project/instances/my-instance/databases/example-db 168
                - Query players with top ten scores within a timespan specified in hours.

প্রতিক্রিয়া থেকে আপনারা দেখতে পাচ্ছেন যে, আমরা সর্বকালের সেরা দশজন খেলোয়াড়ের একটি তালিকা পেতে ` query কমান্ড ব্যবহার করতে পারি। আমরা আরও দেখতে পাচ্ছি যে, querywithtimespan কমান্ডটি আমাদেরকে ` Scores টেবিলের ` Timestamp কলামের মানের উপর ভিত্তি করে রেকর্ড ফিল্টার করার জন্য ঘন্টায় একটি নির্দিষ্ট সময়সীমা উল্লেখ করার সুযোগ দেয়।

চলুন, create কমান্ড চালানোর সময় আমরা যে আর্গুমেন্ট ভ্যালুগুলো ব্যবহার করেছিলাম, সেগুলো দিয়েই query কমান্ডটি চালাই। খেয়াল রাখবেন, এই কোডল্যাবের শুরুতে আপনার তৈরি করা প্রজেক্ট আইডি দিয়ে my-project জায়গাটা অবশ্যই বদলে নেবেন।

./leaderboard query  projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard

আপনি নিম্নলিখিতের মতো সর্বকালের 'সেরা দশ' খেলোয়াড়ের নামসহ একটি উত্তর দেখতে পাবেন:

PlayerId: 4018687297  PlayerName: Player 83  Score: 999,618  Timestamp: 2017-07-01
PlayerId: 4018687297  PlayerName: Player 83  Score: 998,956  Timestamp: 2017-09-02
PlayerId: 4285713246  PlayerName: Player 51  Score: 998,648  Timestamp: 2017-12-01
PlayerId: 5267931774  PlayerName: Player 49  Score: 997,733  Timestamp: 2017-11-09
PlayerId: 1981654448  PlayerName: Player 35  Score: 997,480  Timestamp: 2018-12-06
PlayerId: 4953940705  PlayerName: Player 87  Score: 995,184  Timestamp: 2018-09-14
PlayerId: 2456736905  PlayerName: Player 84  Score: 992,881  Timestamp: 2017-04-14
PlayerId: 8234617611  PlayerName: Player 19  Score: 992,399  Timestamp: 2017-12-27
PlayerId: 1788051688  PlayerName: Player 76  Score: 992,265  Timestamp: 2018-11-22
PlayerId: 7127686505  PlayerName: Player 97  Score: 992,038  Timestamp: 2017-12-02

এখন, বছরের "সেরা দশ" খেলোয়াড়দের খুঁজে বের করার জন্য প্রয়োজনীয় আর্গুমেন্ট সহ ` querywithtimespan কমান্ডটি চালান। এর জন্য `timespan` হিসেবে বছরের মোট ঘন্টার সংখ্যা, অর্থাৎ ৮৭৬০, উল্লেখ করুন। নিশ্চিত করুন যে আপনি my-project এর জায়গায় এই কোডল্যাবের শুরুতে তৈরি করা আপনার প্রজেক্ট আইডিটি বসিয়েছেন।

./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 8760

আপনি নিম্নলিখিতের মতো বছরের 'সেরা দশ' খেলোয়াড়ের নামসহ একটি উত্তর দেখতে পাবেন:

PlayerId: 1981654448  PlayerName: Player 35  Score: 997,480  Timestamp: 2018-12-06
PlayerId: 4953940705  PlayerName: Player 87  Score: 995,184  Timestamp: 2018-09-14
PlayerId: 1788051688  PlayerName: Player 76  Score: 992,265  Timestamp: 2018-11-22
PlayerId: 6862349579  PlayerName: Player 30  Score: 990,877  Timestamp: 2018-09-14
PlayerId: 5529627211  PlayerName: Player 16  Score: 989,142  Timestamp: 2018-03-30
PlayerId: 9743904155  PlayerName: Player 1  Score: 988,765  Timestamp: 2018-05-30
PlayerId: 6809119884  PlayerName: Player 7  Score: 986,673  Timestamp: 2018-05-16
PlayerId: 2132710638  PlayerName: Player 54  Score: 983,108  Timestamp: 2018-09-11
PlayerId: 2320093590  PlayerName: Player 79  Score: 981,373  Timestamp: 2018-05-07
PlayerId: 9554181430  PlayerName: Player 80  Score: 981,087  Timestamp: 2018-06-21

এখন, মাসের সেরা দশজন খেলোয়াড়কে খুঁজে বের করার জন্য ` querywithtimespan কমান্ডটি চালান। এর জন্য `timespan` হিসেবে মাসের মোট ঘন্টার সংখ্যা, অর্থাৎ ৭৩০, উল্লেখ করুন। খেয়াল রাখবেন, my-project জায়গায় যেন এই কোডল্যাবের শুরুতে তৈরি করা আপনার প্রজেক্ট আইডিটি বসে।

./leaderboard querywithtimespan projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 730

আপনি নিচের মতো একটি উত্তর দেখতে পাবেন, যেখানে মাসের 'সেরা দশ' খেলোয়াড়ের নাম অন্তর্ভুক্ত থাকবে:

PlayerId: 3869829195  PlayerName: Player 69  Score: 949,686  Timestamp: 2019-02-19
PlayerId: 7448359883  PlayerName: Player 20  Score: 938,998  Timestamp: 2019-02-07
PlayerId: 1981654448  PlayerName: Player 35  Score: 929,003  Timestamp: 2019-02-22
PlayerId: 9336678658  PlayerName: Player 44  Score: 914,106  Timestamp: 2019-01-27
PlayerId: 6968576389  PlayerName: Player 40  Score: 898,041  Timestamp: 2019-02-21
PlayerId: 5529627211  PlayerName: Player 16  Score: 896,433  Timestamp: 2019-01-29
PlayerId: 9395039625  PlayerName: Player 59  Score: 879,495  Timestamp: 2019-02-09
PlayerId: 2094604854  PlayerName: Player 39  Score: 860,434  Timestamp: 2019-02-01
PlayerId: 9395039625  PlayerName: Player 59  Score: 849,955  Timestamp: 2019-02-21
PlayerId: 4285713246  PlayerName: Player 51  Score: 805,654  Timestamp: 2019-02-02

এখন, সপ্তাহের 'সেরা দশ' খেলোয়াড়কে খুঁজে বের করার জন্য ` querywithtimespan কমান্ডটি চালান। এর জন্য `timespan` হিসেবে সপ্তাহের মোট ঘন্টার সংখ্যা, অর্থাৎ ১৬৮, উল্লেখ করুন। খেয়াল রাখবেন, my-project জায়গায় এই কোডল্যাবের শুরুতে আপনার তৈরি করা প্রজেক্ট আইডিটি বসাবেন।

./leaderboard querywithtimespan  projects/my-project/instances/cloudspanner-leaderboard/databases/leaderboard 168

আপনি নিচের মতো একটি উত্তর দেখতে পাবেন, যেখানে সপ্তাহের 'সেরা দশ' খেলোয়াড়ের নাম অন্তর্ভুক্ত থাকবে:

PlayerId: 3869829195  PlayerName: Player 69  Score: 949,686  Timestamp: 2019-02-19
PlayerId: 1981654448  PlayerName: Player 35  Score: 929,003  Timestamp: 2019-02-22
PlayerId: 6968576389  PlayerName: Player 40  Score: 898,041  Timestamp: 2019-02-21
PlayerId: 9395039625  PlayerName: Player 59  Score: 849,955  Timestamp: 2019-02-21
PlayerId: 5954045812  PlayerName: Player 8  Score: 795,639  Timestamp: 2019-02-22
PlayerId: 3889939638  PlayerName: Player 71  Score: 775,252  Timestamp: 2019-02-21
PlayerId: 5529627211  PlayerName: Player 16  Score: 604,695  Timestamp: 2019-02-19
PlayerId: 9006728426  PlayerName: Player 3  Score: 457,208  Timestamp: 2019-02-22
PlayerId: 8289497066  PlayerName: Player 58  Score: 227,697  Timestamp: 2019-02-20
PlayerId: 8065482904  PlayerName: Player 99  Score: 198,429  Timestamp: 2019-02-24

চমৎকার কাজ!

এখন আপনি রেকর্ড যোগ করার সাথে সাথে ক্লাউড স্প্যানার আপনার ডাটাবেসকে আপনার প্রয়োজন অনুযায়ী বড় করে তুলবে। আপনার ডাটাবেস যতই বড় হোক না কেন, ক্লাউড স্প্যানার এবং এর ট্রুটাইম প্রযুক্তির সাহায্যে আপনার গেমের লিডারবোর্ড নির্ভুলভাবে বড় হতে থাকবে।

৭. পরিচ্ছন্নতা

স্প্যানার নিয়ে এত মজা করার পর, আমাদের প্লেগ্রাউন্ডটি পরিষ্কার করা দরকার, যা মূল্যবান রিসোর্স এবং অর্থ বাঁচাবে। সৌভাগ্যবশত, এটি একটি সহজ পদক্ষেপ; শুধু ক্লাউড কনসোলের ক্লাউড স্প্যানার বিভাগে যান এবং "সেটআপ এ ক্লাউড স্প্যানার ইনস্ট্যান্স" নামের কোডল্যাব ধাপে তৈরি করা ইনস্ট্যান্সটি ডিলিট করে দিন।

৮. অভিনন্দন!

আমরা যা আলোচনা করেছি:

  • লিডারবোর্ডের জন্য গুগল ক্লাউড স্প্যানার ইনস্ট্যান্স, ডেটাবেস এবং টেবিল স্কিমা
  • কিভাবে গো কনসোল অ্যাপ্লিকেশন তৈরি করবেন
  • গো ক্লায়েন্ট লাইব্রেরি ব্যবহার করে কীভাবে স্প্যানার ডেটাবেস এবং টেবিল তৈরি করবেন
  • গো ক্লায়েন্ট লাইব্রেরি ব্যবহার করে স্প্যানার ডেটাবেসে কীভাবে ডেটা লোড করবেন
  • স্প্যানার কমিট টাইমস্ট্যাম্প এবং গো ক্লায়েন্ট লাইব্রেরি ব্যবহার করে আপনার ডেটা থেকে কীভাবে 'শীর্ষ দশ' ফলাফল কোয়েরি করবেন

পরবর্তী পদক্ষেপ:

আপনার মতামত দিন

  • অনুগ্রহ করে একটু সময় নিয়ে আমাদের এই সংক্ষিপ্ত সমীক্ষাটি পূরণ করুন।