From 04a77458495ef6c2c99a36ec7ba0fcfecf5afa92 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Tue, 29 Jun 2021 11:54:14 +0530 Subject: [PATCH 01/10] performance files --- tests/performance/__init__.py | 5 + tests/performance/django_spanner/__init__.py | 0 tests/performance/django_spanner/models.py | 8 + .../django_spanner/test_perfomance.py | 292 ++++++++++++++++++ 4 files changed, 305 insertions(+) create mode 100644 tests/performance/__init__.py create mode 100644 tests/performance/django_spanner/__init__.py create mode 100644 tests/performance/django_spanner/models.py create mode 100644 tests/performance/django_spanner/test_perfomance.py diff --git a/tests/performance/__init__.py b/tests/performance/__init__.py new file mode 100644 index 0000000000..6b607710ed --- /dev/null +++ b/tests/performance/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2020 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd diff --git a/tests/performance/django_spanner/__init__.py b/tests/performance/django_spanner/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/performance/django_spanner/models.py b/tests/performance/django_spanner/models.py new file mode 100644 index 0000000000..83a6bd8273 --- /dev/null +++ b/tests/performance/django_spanner/models.py @@ -0,0 +1,8 @@ +from django.db import models + + +class Author(models.Model): + id = models.IntegerField(primary_key=True) + first_name = models.CharField(max_length=20) + last_name = models.CharField(max_length=20) + rating = models.CharField(max_length=50) diff --git a/tests/performance/django_spanner/test_perfomance.py b/tests/performance/django_spanner/test_perfomance.py new file mode 100644 index 0000000000..ad61c45748 --- /dev/null +++ b/tests/performance/django_spanner/test_perfomance.py @@ -0,0 +1,292 @@ +import datetime +import random +import statistics +import time +from decimal import Decimal +from typing import Any + +from django.db import connection +from django.test import TransactionTestCase +from google.api_core.exceptions import Aborted +from google.cloud import spanner_dbapi +from google.cloud.spanner_v1 import Client, KeySet +from scipy.stats import sem + +from tests.system.django_spanner.utils import ( + setup_database, + setup_instance, + teardown_database, + teardown_instance, +) +import pytest + + + +val="2.1" +from tests.performance.django_spanner.models import Author + + +def measure_execution_time(function): + """Decorator to measure a wrapped method execution time.""" + + def wrapper(self, measures): + """Execute the wrapped method and measure its execution time. + Args: + measures (dict): Test cases and their execution time. + """ + t_start = time.time() + try: + function(self) + measures[function.__name__] = round(time.time() - t_start, 2) + except Aborted: + measures[function.__name__] = 0 + + return wrapper +def insert_one_row(transaction, one_row): + """A transaction-function for the original Spanner client. + Inserts a single row into a database and then fetches it back. + """ + transaction.execute_update( + "INSERT Author (id, first_name, last_name, rating) " + " VALUES {}".format(str(one_row)) + ) + last_name = transaction.execute_sql( + "SELECT last_name FROM Author WHERE id=1" + ).one()[0] + if last_name != "Allison": + raise ValueError("Received invalid last name: " + last_name) + + +def insert_many_rows(transaction, many_rows): + """A transaction-function for the original Spanner client. + Insert 100 rows into a database. + """ + statements = [] + for row in many_rows: + statements.append( + "INSERT Author (id, first_name, last_name, rating) " + " VALUES {}".format(str(row)) + ) + _, count = transaction.batch_update(statements) + if sum(count) != 99: + raise ValueError("Wrong number of inserts: " + str(sum(count))) + + +@pytest.mark.django_db +class BenchmarkTestBase: + """Base class for performance testing. + Organizes testing data preparation and cleanup. + """ + + def __init__(self): + self._create_table() + + self._one_row = ( + 1, + "Pete", + "Allison", + val, + ) + + def _cleanup(self): + """Drop the test table.""" + conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") + conn.database.update_ddl(["DROP TABLE Author"]) + conn.close() + + def _create_table(self): + """Create a table for performace testing.""" + conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") + conn.database.update_ddl( + [ + """ +CREATE TABLE Author ( + id INT64, + first_name STRING(20), + last_name STRING(20), + rating STRING(50), +) PRIMARY KEY (id) + """ + ] + ).result(120) + + conn.close() + + def run(self): + """Execute every test case.""" + measures = {} + for method in ( + self.insert_one_row_with_fetch_after, + self.read_one_row, + self.insert_many_rows, + self.select_many_rows, + self.insert_many_rows_with_mutations, + ): + method(measures) + + self._cleanup() + return measures + + +@pytest.mark.django_db +class SpannerBenchmarkTest(BenchmarkTestBase): + """The original Spanner performace testing class.""" + def __init__(self): + super().__init__() + self._client = Client() + self._instance = self._client.instance("django-spanner-test") + self._database = self._instance.database("spanner-testdb") + + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + num = round(random.random() * 1000000) + self._many_rows.append((num, "Pete", "Allison", val)) + num2 = round(random.random() * 1000000) + self._many_rows2.append((num2, "Pete", "Allison", val)) + + # initiate a session + with self._database.snapshot(): + pass + + @measure_execution_time + def insert_one_row_with_fetch_after(self): + self._database.run_in_transaction(insert_one_row, self._one_row) + + @measure_execution_time + def insert_many_rows(self): + self._database.run_in_transaction(insert_many_rows, self._many_rows) + + @measure_execution_time + def insert_many_rows_with_mutations(self): + with self._database.batch() as batch: + batch.insert( + table="Author", + columns=("id", "first_name", "last_name", "rating"), + values=self._many_rows2, + ) + + @measure_execution_time + def read_one_row(self): + with self._database.snapshot() as snapshot: + keyset = KeySet(all_=True) + snapshot.read( + table="Author", + columns=("id", "first_name", "last_name", "rating"), + keyset=keyset, + ).one() + + @measure_execution_time + def select_many_rows(self): + with self._database.snapshot() as snapshot: + rows = list( + snapshot.execute_sql("SELECT * FROM Author ORDER BY last_name") + ) + if len(rows) != 100: + raise ValueError("Wrong number of rows read") + +@pytest.mark.django_db +class DjangoBenchmarkTest(BenchmarkTestBase): + def __init__(self): + setup_instance() + setup_database() + with connection.schema_editor() as editor: + editor.create_model(Author) + + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + self._many_rows.append(Author("Pete", "Allison", val)) + self._many_rows2.append(Author("Pete", "Allison", val)) + + def _cleanup(self): + """Drop the test table.""" + with connection.schema_editor() as editor: + editor.delete_model(Author) + + def __del__(self): + teardown_database() + teardown_instance() + + @measure_execution_time + def insert_one_row_with_fetch_after(self): + author_kent = Author( + first_name="Pete", last_name="Allison", rating=val, + ) + author_kent.save() + last_name = Author.objects.get(pk=author_kent.id).last_name + if last_name != "Allison": + raise ValueError("Received invalid last name: " + last_name) + + @measure_execution_time + def insert_many_rows(self): + Author.objects.bulk_create(self._many_rows) + + @measure_execution_time + def insert_many_rows_with_mutations(self): + pass + + @measure_execution_time + def read_one_row(self): + row = Author.objects.all().first() + if row is None: + raise ValueError("No rows read") + + @measure_execution_time + def select_many_rows(self): + rows = Author.objects.all() + if len(rows) != 100: + raise ValueError("Wrong number of rows read") + + +def compare_measurements(spanner, django): + """ + Compare the original Spanner client performance measures + with Spanner dialect for Django ones. + """ + comparison = {} + for key in django.keys(): + comparison[key] = { + "Spanner, sec": spanner[key], + "Django, sec": django[key], + "Django deviation": round(django[key] - spanner[key], 2), + "Django to Spanner, %": round(django[key] / spanner[key] * 100), + } + return comparison + + +measures = [] +for _ in range(50): + #spanner_measures = SpannerBenchmarkTest().run() + django_measures = DjangoBenchmarkTest().run() + #measures.append((spanner_measures, django_measures)) + +agg = {"spanner": {}, "django": {}} + +for span, djan in measures: + for key, value in span.items(): + #agg["spanner"].setdefault(key, []).append(value) + agg["django"].setdefault(key, []).append(djan[key]) + +# spanner_stats = {} +# for key, value in agg["spanner"].items(): +# while 0 in value: +# value.remove(0) +# spanner_stats[key + "_aver"] = round(statistics.mean(value), 2) +# spanner_stats[key + "_error"] = round(sem(value), 2) +# spanner_stats[key + "_std_dev"] = round(statistics.pstdev(value), 2) + +django_stats = {} +for key, value in agg["django"].items(): + while 0 in value: + value.remove(0) + django_stats[key + "_aver"] = round(statistics.mean(value), 2) + django_stats[key + "_error"] = round(sem(value), 2) + django_stats[key + "_std_dev"] = round(statistics.pstdev(value), 2) + +# for key in spanner_stats: +# print(key + ":") +# print("spanner: ", spanner_stats[key]) +# print("django: ", django_stats[key]) + + From 7a99ca183b9446673362cd61d4286911bb4d2baf Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Mon, 5 Jul 2021 12:18:32 +0530 Subject: [PATCH 02/10] test_benchmark --- .../django_spanner/test_benchmark.py | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 tests/performance/django_spanner/test_benchmark.py diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py new file mode 100644 index 0000000000..497f0b85a0 --- /dev/null +++ b/tests/performance/django_spanner/test_benchmark.py @@ -0,0 +1,256 @@ +import datetime +import random +import statistics +import time +from decimal import Decimal +from typing import Any + +from django.db import connection +from django.test import TransactionTestCase +from google.api_core.exceptions import Aborted +from google.cloud import spanner_dbapi +from google.cloud.spanner_v1 import Client, KeySet +from scipy.stats import sem + +from tests.system.django_spanner.utils import ( + setup_database, + setup_instance, + teardown_database, + teardown_instance, +) +import pytest + + +val = "2.1" +from tests.performance.django_spanner.models import Author + + +def measure_execution_time(function): + """Decorator to measure a wrapped method execution time.""" + + def wrapper(self, measures): + """Execute the wrapped method and measure its execution time. + Args: + measures (dict): Test cases and their execution time. + """ + t_start = time.time() + try: + function(self) + measures[function.__name__] = round(time.time() - t_start, 2) + except Aborted: + measures[function.__name__] = 0 + + return wrapper + + +def insert_one_row(transaction, one_row): + """A transaction-function for the original Spanner client. + Inserts a single row into a database and then fetches it back. + """ + transaction.execute_update( + "INSERT Author (id, first_name, last_name, rating) " + " VALUES {}".format(str(one_row)) + ) + last_name = transaction.execute_sql( + "SELECT last_name FROM Author WHERE id=1" + ).one()[0] + if last_name != "Allison": + raise ValueError("Received invalid last name: " + last_name) + + +def insert_many_rows(transaction, many_rows): + """A transaction-function for the original Spanner client. + Insert 100 rows into a database. + """ + statements = [] + for row in many_rows: + statements.append( + "INSERT Author (id, first_name, last_name, rating) " + " VALUES {}".format(str(row)) + ) + _, count = transaction.batch_update(statements) + if sum(count) != 99: + raise ValueError("Wrong number of inserts: " + str(sum(count))) + + +class DjangoBenchmarkTest(TransactionTestCase): + @classmethod + def setUpClass(cls): + setup_instance() + setup_database() + with connection.schema_editor() as editor: + # Create the tables + editor.create_model(Author) + + @classmethod + def tearDownClass(cls): + with connection.schema_editor() as editor: + # delete the table + editor.delete_model(Author) + teardown_database() + # teardown_instance() + + def setUp(self): + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + num = round(random.random() * 1000000) + self._many_rows.append(Author(num, "Pete", "Allison", val)) + num2 = round(random.random() * 1000000) + self._many_rows2.append(Author(num2, "Pete", "Allison", val)) + + @measure_execution_time + def insert_one_row_with_fetch_after(self): + author_kent = Author( + id=2, + first_name="Pete", + last_name="Allison", + rating=val, + ) + author_kent.save() + last_name = Author.objects.get(pk=author_kent.id).last_name + if last_name != "Allison": + raise ValueError("Received invalid last name: " + last_name) + + @measure_execution_time + def insert_many_rows(self): + for row in self._many_rows: + row.save() + + @measure_execution_time + def insert_many_rows_with_mutations(self): + Author.objects.bulk_create(self._many_rows2) + + @measure_execution_time + def read_one_row(self): + row = Author.objects.all().first() + if row is None: + raise ValueError("No rows read") + + @measure_execution_time + def select_many_rows(self): + rows = Author.objects.all() + if len(rows) != 100: + raise ValueError("Wrong number of rows read") + + def test_run(self): + """Execute every test case.""" + measures = {} + for method in ( + self.insert_one_row_with_fetch_after, + self.read_one_row, + self.insert_many_rows, + self.select_many_rows, + self.insert_many_rows_with_mutations, + ): + method(measures) + import pdb + pdb.set_trace() + return measures + + +class SpannerBenchmarkTest(TransactionTestCase): + """The original Spanner performace testing class.""" + def setUp(self): + self._create_table() + self._one_row = ( + 1, + "Pete", + "Allison", + val, + ) + self._client = Client() + self._instance = self._client.instance("django-spanner-test") + self._database = self._instance.database("spanner-testdb") + + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + num = round(random.random() * 1000000) + self._many_rows.append((num, "Pete", "Allison", val)) + num2 = round(random.random() * 1000000) + self._many_rows2.append((num2, "Pete", "Allison", val)) + + # initiate a session + with self._database.snapshot(): + pass + def _create_table(self): + """Create a table for performace testing.""" + conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") + conn.database.update_ddl( + [ + """ +CREATE TABLE Author ( + id INT64, + first_name STRING(20), + last_name STRING(20), + rating STRING(50), +) PRIMARY KEY (id) + """ + ] + ).result(120) + + conn.close() + + @measure_execution_time + def insert_one_row_with_fetch_after(self): + self._database.run_in_transaction(insert_one_row, self._one_row) + + @measure_execution_time + def insert_many_rows(self): + self._database.run_in_transaction(insert_many_rows, self._many_rows) + + @measure_execution_time + def insert_many_rows_with_mutations(self): + with self._database.batch() as batch: + batch.insert( + table="Author", + columns=("id", "first_name", "last_name", "rating"), + values=self._many_rows2, + ) + + @measure_execution_time + def read_one_row(self): + with self._database.snapshot() as snapshot: + keyset = KeySet(all_=True) + snapshot.read( + table="Author", + columns=("id", "first_name", "last_name", "rating"), + keyset=keyset, + ).one() + + @measure_execution_time + def select_many_rows(self): + with self._database.snapshot() as snapshot: + rows = list( + snapshot.execute_sql("SELECT * FROM Author ORDER BY last_name") + ) + if len(rows) != 100: + raise ValueError("Wrong number of rows read") + + def _cleanup(self): + """Drop the test table.""" + conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") + conn.database.update_ddl(["DROP TABLE Author"]) + conn.close() + + def test_run(self): + """Execute every test case.""" + measures = {} + for method in ( + self.insert_one_row_with_fetch_after, + self.read_one_row, + self.insert_many_rows, + self.select_many_rows, + self.insert_many_rows_with_mutations, + ): + method(measures) + self._cleanup() + # import pdb + # pdb.set_trace() + return measures + + + + +#print(DjangoBenchmarkTest().test_run()) From 79d94f7239db962709fd04d331fca52b7e36662e Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Wed, 7 Jul 2021 11:11:58 +0530 Subject: [PATCH 03/10] performance testing changes --- .../django_spanner/test_benchmark.py | 93 +++++++++++-------- tests/system/django_spanner/utils.py | 10 +- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py index 497f0b85a0..88597ed410 100644 --- a/tests/performance/django_spanner/test_benchmark.py +++ b/tests/performance/django_spanner/test_benchmark.py @@ -4,6 +4,9 @@ import time from decimal import Decimal from typing import Any +import unittest +import pandas as pd +from tests.settings import INSTANCE_ID, DATABASE_NAME from django.db import connection from django.test import TransactionTestCase @@ -21,7 +24,6 @@ import pytest -val = "2.1" from tests.performance.django_spanner.models import Author @@ -36,7 +38,7 @@ def wrapper(self, measures): t_start = time.time() try: function(self) - measures[function.__name__] = round(time.time() - t_start, 2) + measures[function.__name__] = round(time.time() - t_start, 4) except Aborted: measures[function.__name__] = 0 @@ -73,39 +75,36 @@ def insert_many_rows(transaction, many_rows): raise ValueError("Wrong number of inserts: " + str(sum(count))) -class DjangoBenchmarkTest(TransactionTestCase): - @classmethod - def setUpClass(cls): - setup_instance() +class DjangoBenchmarkTest(): + def __init__(self): setup_database() with connection.schema_editor() as editor: # Create the tables editor.create_model(Author) - @classmethod - def tearDownClass(cls): + self._many_rows = [] + self._many_rows2 = [] + for i in range(99): + num = round(random.randint(0,100000000)) + self._many_rows.append(Author(num, "Pete", "Allison", "2.1")) + num2 = round(random.randint(0,100000000)) + self._many_rows2.append(Author(num2, "Pete", "Allison", "2.1")) + + def _cleanup(self): + """Drop the test table.""" with connection.schema_editor() as editor: # delete the table editor.delete_model(Author) teardown_database() # teardown_instance() - def setUp(self): - self._many_rows = [] - self._many_rows2 = [] - for i in range(99): - num = round(random.random() * 1000000) - self._many_rows.append(Author(num, "Pete", "Allison", val)) - num2 = round(random.random() * 1000000) - self._many_rows2.append(Author(num2, "Pete", "Allison", val)) - @measure_execution_time def insert_one_row_with_fetch_after(self): author_kent = Author( id=2, first_name="Pete", last_name="Allison", - rating=val, + rating="2.1", ) author_kent.save() last_name = Author.objects.get(pk=author_kent.id).last_name @@ -133,7 +132,7 @@ def select_many_rows(self): if len(rows) != 100: raise ValueError("Wrong number of rows read") - def test_run(self): + def run(self): """Execute every test case.""" measures = {} for method in ( @@ -144,39 +143,41 @@ def test_run(self): self.insert_many_rows_with_mutations, ): method(measures) - import pdb - pdb.set_trace() + self._cleanup() + # import pdb + # pdb.set_trace() return measures -class SpannerBenchmarkTest(TransactionTestCase): +class SpannerBenchmarkTest(): """The original Spanner performace testing class.""" - def setUp(self): + def __init__(self): + setup_database() self._create_table() self._one_row = ( 1, "Pete", "Allison", - val, + "2.1", ) self._client = Client() - self._instance = self._client.instance("django-spanner-test") - self._database = self._instance.database("spanner-testdb") + self._instance = self._client.instance(INSTANCE_ID) + self._database = self._instance.database(DATABASE_NAME) self._many_rows = [] self._many_rows2 = [] for i in range(99): - num = round(random.random() * 1000000) - self._many_rows.append((num, "Pete", "Allison", val)) - num2 = round(random.random() * 1000000) - self._many_rows2.append((num2, "Pete", "Allison", val)) + num = round(random.randint(0,100000000)) + self._many_rows.append((num, "Pete", "Allison", "2.1")) + num2 = round(random.randint(0,100000000)) + self._many_rows2.append((num2, "Pete", "Allison", "2.1")) # initiate a session with self._database.snapshot(): pass def _create_table(self): """Create a table for performace testing.""" - conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") + conn = spanner_dbapi.connect(INSTANCE_ID, DATABASE_NAME) conn.database.update_ddl( [ """ @@ -230,11 +231,11 @@ def select_many_rows(self): def _cleanup(self): """Drop the test table.""" - conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") + conn = spanner_dbapi.connect(INSTANCE_ID, DATABASE_NAME) conn.database.update_ddl(["DROP TABLE Author"]) conn.close() - def test_run(self): + def run(self): """Execute every test case.""" measures = {} for method in ( @@ -246,11 +247,29 @@ def test_run(self): ): method(measures) self._cleanup() - # import pdb - # pdb.set_trace() return measures +@pytest.mark.django_db(transaction=True) +class BenchmarkTest(unittest.TestCase): + + def test_run(self): + setup_instance() + django_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', + 'insert_many_rows_with_mutations']) + spanner_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', + 'insert_many_rows_with_mutations']) + + for _ in range(2): + django_obj = django_obj.append(DjangoBenchmarkTest().run(), ignore_index=True) + spanner_obj = spanner_obj.append(SpannerBenchmarkTest().run(), ignore_index=True) + + django_avg = django_obj.mean(axis = 0) + spanner_avg = spanner_obj.mean(axis = 0) + django_std = django_obj.std(axis = 0) + spanner_std = spanner_obj.std(axis = 0) + django_err = django_obj.sem(axis = 0) + spanner_err = spanner_obj.sem(axis = 0) + print("Django Average: ", django_avg, "\n Spanner Average: ", spanner_avg, "\n Django Standard Deviation: ", django_std, + "\n Spanner Standard Deviation: ", spanner_std, "\n Django Error: ", django_err, "\n Spanner Error: ", spanner_err, sep='\n') - -#print(DjangoBenchmarkTest().test_run()) diff --git a/tests/system/django_spanner/utils.py b/tests/system/django_spanner/utils.py index 7fac5166e0..ad001378f5 100644 --- a/tests/system/django_spanner/utils.py +++ b/tests/system/django_spanner/utils.py @@ -13,7 +13,7 @@ from google.cloud.spanner_v1 import Client from google.cloud.spanner_v1.instance import Instance, Backup from test_utils.retry import RetryErrors - +import pytest from django_spanner.creation import DatabaseCreation CREATE_INSTANCE = ( @@ -30,7 +30,7 @@ PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT", "emulator-test-project") DATABASE_NAME = os.getenv("DJANGO_SPANNER_DB", "django_test_db") - +@pytest.mark.django_db(transaction=True) class Config(object): """Run-time configuration to be modified at set-up. @@ -47,7 +47,7 @@ class Config(object): def _list_instances(): return list(Config.CLIENT.list_instances()) - +@pytest.mark.django_db(transaction=True) def setup_instance(): if USE_EMULATOR: from google.auth.credentials import AnonymousCredentials @@ -118,7 +118,7 @@ def teardown_instance(): if CREATE_INSTANCE: Config.INSTANCE.delete() - +@pytest.mark.django_db(transaction=True) def setup_database(): Config.DATABASE = Config.INSTANCE.database(DATABASE_NAME) if not Config.DATABASE.exists(): @@ -126,7 +126,7 @@ def setup_database(): creation._create_test_db(verbosity=0, autoclobber=False, keepdb=True) # Running migrations on the db. - call_command("migrate", interactive=False) + #call_command("migrate", interactive=False) def teardown_database(): From aad550cf75de5445ed16b699ea41ee2da0e87082 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Thu, 8 Jul 2021 01:25:09 +0530 Subject: [PATCH 04/10] changes in benchmark performance for prod --- .../django_spanner/test_benchmark.py | 47 +-- .../django_spanner/test_perfomance.py | 292 ------------------ tests/system/django_spanner/utils.py | 2 +- 3 files changed, 17 insertions(+), 324 deletions(-) delete mode 100644 tests/performance/django_spanner/test_perfomance.py diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py index 88597ed410..821bf64e82 100644 --- a/tests/performance/django_spanner/test_benchmark.py +++ b/tests/performance/django_spanner/test_benchmark.py @@ -1,30 +1,18 @@ -import datetime import random -import statistics import time -from decimal import Decimal -from typing import Any import unittest -import pandas as pd -from tests.settings import INSTANCE_ID, DATABASE_NAME +from typing import Any +import pandas as pd +import pytest from django.db import connection -from django.test import TransactionTestCase from google.api_core.exceptions import Aborted from google.cloud import spanner_dbapi from google.cloud.spanner_v1 import Client, KeySet -from scipy.stats import sem - -from tests.system.django_spanner.utils import ( - setup_database, - setup_instance, - teardown_database, - teardown_instance, -) -import pytest - from tests.performance.django_spanner.models import Author +from tests.settings import DATABASE_NAME, INSTANCE_ID +from tests.system.django_spanner.utils import setup_database, setup_instance def measure_execution_time(function): @@ -77,7 +65,6 @@ def insert_many_rows(transaction, many_rows): class DjangoBenchmarkTest(): def __init__(self): - setup_database() with connection.schema_editor() as editor: # Create the tables editor.create_model(Author) @@ -95,7 +82,6 @@ def _cleanup(self): with connection.schema_editor() as editor: # delete the table editor.delete_model(Author) - teardown_database() # teardown_instance() @measure_execution_time @@ -152,7 +138,6 @@ def run(self): class SpannerBenchmarkTest(): """The original Spanner performace testing class.""" def __init__(self): - setup_database() self._create_table() self._one_row = ( 1, @@ -249,11 +234,12 @@ def run(self): self._cleanup() return measures -@pytest.mark.django_db(transaction=True) +@pytest.mark.django_db() class BenchmarkTest(unittest.TestCase): def test_run(self): setup_instance() + setup_database() django_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', 'insert_many_rows_with_mutations']) spanner_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', @@ -263,13 +249,12 @@ def test_run(self): django_obj = django_obj.append(DjangoBenchmarkTest().run(), ignore_index=True) spanner_obj = spanner_obj.append(SpannerBenchmarkTest().run(), ignore_index=True) - django_avg = django_obj.mean(axis = 0) - spanner_avg = spanner_obj.mean(axis = 0) - django_std = django_obj.std(axis = 0) - spanner_std = spanner_obj.std(axis = 0) - django_err = django_obj.sem(axis = 0) - spanner_err = spanner_obj.sem(axis = 0) - print("Django Average: ", django_avg, "\n Spanner Average: ", spanner_avg, "\n Django Standard Deviation: ", django_std, - "\n Spanner Standard Deviation: ", spanner_std, "\n Django Error: ", django_err, "\n Spanner Error: ", spanner_err, sep='\n') - - + avg = pd.concat([django_obj.mean(axis = 0), spanner_obj.mean(axis = 0)], axis=1) + avg.columns=['Django','Spanner'] + std = pd.concat([django_obj.std(axis = 0), spanner_obj.std(axis = 0)], axis=1) + std.columns=['Django','Spanner'] + err = pd.concat([django_obj.sem(axis = 0), spanner_obj.sem(axis = 0)], axis=1) + err.columns=['Django','Spanner'] + + print("Average: ", avg, "Standard Deviation: ", std, "Error:", err, sep='\n') + diff --git a/tests/performance/django_spanner/test_perfomance.py b/tests/performance/django_spanner/test_perfomance.py deleted file mode 100644 index ad61c45748..0000000000 --- a/tests/performance/django_spanner/test_perfomance.py +++ /dev/null @@ -1,292 +0,0 @@ -import datetime -import random -import statistics -import time -from decimal import Decimal -from typing import Any - -from django.db import connection -from django.test import TransactionTestCase -from google.api_core.exceptions import Aborted -from google.cloud import spanner_dbapi -from google.cloud.spanner_v1 import Client, KeySet -from scipy.stats import sem - -from tests.system.django_spanner.utils import ( - setup_database, - setup_instance, - teardown_database, - teardown_instance, -) -import pytest - - - -val="2.1" -from tests.performance.django_spanner.models import Author - - -def measure_execution_time(function): - """Decorator to measure a wrapped method execution time.""" - - def wrapper(self, measures): - """Execute the wrapped method and measure its execution time. - Args: - measures (dict): Test cases and their execution time. - """ - t_start = time.time() - try: - function(self) - measures[function.__name__] = round(time.time() - t_start, 2) - except Aborted: - measures[function.__name__] = 0 - - return wrapper -def insert_one_row(transaction, one_row): - """A transaction-function for the original Spanner client. - Inserts a single row into a database and then fetches it back. - """ - transaction.execute_update( - "INSERT Author (id, first_name, last_name, rating) " - " VALUES {}".format(str(one_row)) - ) - last_name = transaction.execute_sql( - "SELECT last_name FROM Author WHERE id=1" - ).one()[0] - if last_name != "Allison": - raise ValueError("Received invalid last name: " + last_name) - - -def insert_many_rows(transaction, many_rows): - """A transaction-function for the original Spanner client. - Insert 100 rows into a database. - """ - statements = [] - for row in many_rows: - statements.append( - "INSERT Author (id, first_name, last_name, rating) " - " VALUES {}".format(str(row)) - ) - _, count = transaction.batch_update(statements) - if sum(count) != 99: - raise ValueError("Wrong number of inserts: " + str(sum(count))) - - -@pytest.mark.django_db -class BenchmarkTestBase: - """Base class for performance testing. - Organizes testing data preparation and cleanup. - """ - - def __init__(self): - self._create_table() - - self._one_row = ( - 1, - "Pete", - "Allison", - val, - ) - - def _cleanup(self): - """Drop the test table.""" - conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") - conn.database.update_ddl(["DROP TABLE Author"]) - conn.close() - - def _create_table(self): - """Create a table for performace testing.""" - conn = spanner_dbapi.connect("django-spanner-test", "spanner-testdb") - conn.database.update_ddl( - [ - """ -CREATE TABLE Author ( - id INT64, - first_name STRING(20), - last_name STRING(20), - rating STRING(50), -) PRIMARY KEY (id) - """ - ] - ).result(120) - - conn.close() - - def run(self): - """Execute every test case.""" - measures = {} - for method in ( - self.insert_one_row_with_fetch_after, - self.read_one_row, - self.insert_many_rows, - self.select_many_rows, - self.insert_many_rows_with_mutations, - ): - method(measures) - - self._cleanup() - return measures - - -@pytest.mark.django_db -class SpannerBenchmarkTest(BenchmarkTestBase): - """The original Spanner performace testing class.""" - def __init__(self): - super().__init__() - self._client = Client() - self._instance = self._client.instance("django-spanner-test") - self._database = self._instance.database("spanner-testdb") - - self._many_rows = [] - self._many_rows2 = [] - for i in range(99): - num = round(random.random() * 1000000) - self._many_rows.append((num, "Pete", "Allison", val)) - num2 = round(random.random() * 1000000) - self._many_rows2.append((num2, "Pete", "Allison", val)) - - # initiate a session - with self._database.snapshot(): - pass - - @measure_execution_time - def insert_one_row_with_fetch_after(self): - self._database.run_in_transaction(insert_one_row, self._one_row) - - @measure_execution_time - def insert_many_rows(self): - self._database.run_in_transaction(insert_many_rows, self._many_rows) - - @measure_execution_time - def insert_many_rows_with_mutations(self): - with self._database.batch() as batch: - batch.insert( - table="Author", - columns=("id", "first_name", "last_name", "rating"), - values=self._many_rows2, - ) - - @measure_execution_time - def read_one_row(self): - with self._database.snapshot() as snapshot: - keyset = KeySet(all_=True) - snapshot.read( - table="Author", - columns=("id", "first_name", "last_name", "rating"), - keyset=keyset, - ).one() - - @measure_execution_time - def select_many_rows(self): - with self._database.snapshot() as snapshot: - rows = list( - snapshot.execute_sql("SELECT * FROM Author ORDER BY last_name") - ) - if len(rows) != 100: - raise ValueError("Wrong number of rows read") - -@pytest.mark.django_db -class DjangoBenchmarkTest(BenchmarkTestBase): - def __init__(self): - setup_instance() - setup_database() - with connection.schema_editor() as editor: - editor.create_model(Author) - - self._many_rows = [] - self._many_rows2 = [] - for i in range(99): - self._many_rows.append(Author("Pete", "Allison", val)) - self._many_rows2.append(Author("Pete", "Allison", val)) - - def _cleanup(self): - """Drop the test table.""" - with connection.schema_editor() as editor: - editor.delete_model(Author) - - def __del__(self): - teardown_database() - teardown_instance() - - @measure_execution_time - def insert_one_row_with_fetch_after(self): - author_kent = Author( - first_name="Pete", last_name="Allison", rating=val, - ) - author_kent.save() - last_name = Author.objects.get(pk=author_kent.id).last_name - if last_name != "Allison": - raise ValueError("Received invalid last name: " + last_name) - - @measure_execution_time - def insert_many_rows(self): - Author.objects.bulk_create(self._many_rows) - - @measure_execution_time - def insert_many_rows_with_mutations(self): - pass - - @measure_execution_time - def read_one_row(self): - row = Author.objects.all().first() - if row is None: - raise ValueError("No rows read") - - @measure_execution_time - def select_many_rows(self): - rows = Author.objects.all() - if len(rows) != 100: - raise ValueError("Wrong number of rows read") - - -def compare_measurements(spanner, django): - """ - Compare the original Spanner client performance measures - with Spanner dialect for Django ones. - """ - comparison = {} - for key in django.keys(): - comparison[key] = { - "Spanner, sec": spanner[key], - "Django, sec": django[key], - "Django deviation": round(django[key] - spanner[key], 2), - "Django to Spanner, %": round(django[key] / spanner[key] * 100), - } - return comparison - - -measures = [] -for _ in range(50): - #spanner_measures = SpannerBenchmarkTest().run() - django_measures = DjangoBenchmarkTest().run() - #measures.append((spanner_measures, django_measures)) - -agg = {"spanner": {}, "django": {}} - -for span, djan in measures: - for key, value in span.items(): - #agg["spanner"].setdefault(key, []).append(value) - agg["django"].setdefault(key, []).append(djan[key]) - -# spanner_stats = {} -# for key, value in agg["spanner"].items(): -# while 0 in value: -# value.remove(0) -# spanner_stats[key + "_aver"] = round(statistics.mean(value), 2) -# spanner_stats[key + "_error"] = round(sem(value), 2) -# spanner_stats[key + "_std_dev"] = round(statistics.pstdev(value), 2) - -django_stats = {} -for key, value in agg["django"].items(): - while 0 in value: - value.remove(0) - django_stats[key + "_aver"] = round(statistics.mean(value), 2) - django_stats[key + "_error"] = round(sem(value), 2) - django_stats[key + "_std_dev"] = round(statistics.pstdev(value), 2) - -# for key in spanner_stats: -# print(key + ":") -# print("spanner: ", spanner_stats[key]) -# print("django: ", django_stats[key]) - - diff --git a/tests/system/django_spanner/utils.py b/tests/system/django_spanner/utils.py index ad001378f5..189f1fea47 100644 --- a/tests/system/django_spanner/utils.py +++ b/tests/system/django_spanner/utils.py @@ -126,7 +126,7 @@ def setup_database(): creation._create_test_db(verbosity=0, autoclobber=False, keepdb=True) # Running migrations on the db. - #call_command("migrate", interactive=False) + call_command("migrate", interactive=False) def teardown_database(): From ba0faeeb18c3b2a67f177f607751e40507af5fe4 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Thu, 8 Jul 2021 01:32:13 +0530 Subject: [PATCH 05/10] changes to number of runs --- tests/performance/django_spanner/test_benchmark.py | 2 +- tests/system/django_spanner/utils.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py index 821bf64e82..27250b34db 100644 --- a/tests/performance/django_spanner/test_benchmark.py +++ b/tests/performance/django_spanner/test_benchmark.py @@ -245,7 +245,7 @@ def test_run(self): spanner_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', 'insert_many_rows_with_mutations']) - for _ in range(2): + for _ in range(50): django_obj = django_obj.append(DjangoBenchmarkTest().run(), ignore_index=True) spanner_obj = spanner_obj.append(SpannerBenchmarkTest().run(), ignore_index=True) diff --git a/tests/system/django_spanner/utils.py b/tests/system/django_spanner/utils.py index 189f1fea47..7fac5166e0 100644 --- a/tests/system/django_spanner/utils.py +++ b/tests/system/django_spanner/utils.py @@ -13,7 +13,7 @@ from google.cloud.spanner_v1 import Client from google.cloud.spanner_v1.instance import Instance, Backup from test_utils.retry import RetryErrors -import pytest + from django_spanner.creation import DatabaseCreation CREATE_INSTANCE = ( @@ -30,7 +30,7 @@ PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT", "emulator-test-project") DATABASE_NAME = os.getenv("DJANGO_SPANNER_DB", "django_test_db") -@pytest.mark.django_db(transaction=True) + class Config(object): """Run-time configuration to be modified at set-up. @@ -47,7 +47,7 @@ class Config(object): def _list_instances(): return list(Config.CLIENT.list_instances()) -@pytest.mark.django_db(transaction=True) + def setup_instance(): if USE_EMULATOR: from google.auth.credentials import AnonymousCredentials @@ -118,7 +118,7 @@ def teardown_instance(): if CREATE_INSTANCE: Config.INSTANCE.delete() -@pytest.mark.django_db(transaction=True) + def setup_database(): Config.DATABASE = Config.INSTANCE.database(DATABASE_NAME) if not Config.DATABASE.exists(): From 155aa13145674c160db86e02d252bdf3e94e6705 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Thu, 8 Jul 2021 01:40:31 +0530 Subject: [PATCH 06/10] adding comments --- .../django_spanner/test_benchmark.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py index 27250b34db..304cf50ad3 100644 --- a/tests/performance/django_spanner/test_benchmark.py +++ b/tests/performance/django_spanner/test_benchmark.py @@ -64,6 +64,7 @@ def insert_many_rows(transaction, many_rows): class DjangoBenchmarkTest(): + """The Django performace testing class.""" def __init__(self): with connection.schema_editor() as editor: # Create the tables @@ -77,13 +78,6 @@ def __init__(self): num2 = round(random.randint(0,100000000)) self._many_rows2.append(Author(num2, "Pete", "Allison", "2.1")) - def _cleanup(self): - """Drop the test table.""" - with connection.schema_editor() as editor: - # delete the table - editor.delete_model(Author) - # teardown_instance() - @measure_execution_time def insert_one_row_with_fetch_after(self): author_kent = Author( @@ -118,6 +112,12 @@ def select_many_rows(self): if len(rows) != 100: raise ValueError("Wrong number of rows read") + def _cleanup(self): + """Drop the test table.""" + with connection.schema_editor() as editor: + # delete the table + editor.delete_model(Author) + def run(self): """Execute every test case.""" measures = {} @@ -130,13 +130,11 @@ def run(self): ): method(measures) self._cleanup() - # import pdb - # pdb.set_trace() return measures class SpannerBenchmarkTest(): - """The original Spanner performace testing class.""" + """The Spanner performace testing class.""" def __init__(self): self._create_table() self._one_row = ( @@ -236,10 +234,11 @@ def run(self): @pytest.mark.django_db() class BenchmarkTest(unittest.TestCase): - - def test_run(self): + def setUp(self): setup_instance() setup_database() + + def test_run(self): django_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', 'insert_many_rows_with_mutations']) spanner_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', From 25f9a75c6751a484f37f3dcc61962aee9c14e997 Mon Sep 17 00:00:00 2001 From: Astha Mohta Date: Thu, 8 Jul 2021 01:51:56 +0530 Subject: [PATCH 07/10] linting changes --- .../django_spanner/test_benchmark.py | 85 +++++++++++++------ 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py index 304cf50ad3..1d86e87c48 100644 --- a/tests/performance/django_spanner/test_benchmark.py +++ b/tests/performance/django_spanner/test_benchmark.py @@ -1,7 +1,6 @@ import random import time import unittest -from typing import Any import pandas as pd import pytest @@ -63,8 +62,9 @@ def insert_many_rows(transaction, many_rows): raise ValueError("Wrong number of inserts: " + str(sum(count))) -class DjangoBenchmarkTest(): +class DjangoBenchmarkTest: """The Django performace testing class.""" + def __init__(self): with connection.schema_editor() as editor: # Create the tables @@ -73,18 +73,15 @@ def __init__(self): self._many_rows = [] self._many_rows2 = [] for i in range(99): - num = round(random.randint(0,100000000)) + num = round(random.randint(0, 100000000)) self._many_rows.append(Author(num, "Pete", "Allison", "2.1")) - num2 = round(random.randint(0,100000000)) + num2 = round(random.randint(0, 100000000)) self._many_rows2.append(Author(num2, "Pete", "Allison", "2.1")) @measure_execution_time def insert_one_row_with_fetch_after(self): author_kent = Author( - id=2, - first_name="Pete", - last_name="Allison", - rating="2.1", + id=2, first_name="Pete", last_name="Allison", rating="2.1", ) author_kent.save() last_name = Author.objects.get(pk=author_kent.id).last_name @@ -133,8 +130,9 @@ def run(self): return measures -class SpannerBenchmarkTest(): +class SpannerBenchmarkTest: """The Spanner performace testing class.""" + def __init__(self): self._create_table() self._one_row = ( @@ -150,14 +148,15 @@ def __init__(self): self._many_rows = [] self._many_rows2 = [] for i in range(99): - num = round(random.randint(0,100000000)) + num = round(random.randint(0, 100000000)) self._many_rows.append((num, "Pete", "Allison", "2.1")) - num2 = round(random.randint(0,100000000)) + num2 = round(random.randint(0, 100000000)) self._many_rows2.append((num2, "Pete", "Allison", "2.1")) # initiate a session with self._database.snapshot(): pass + def _create_table(self): """Create a table for performace testing.""" conn = spanner_dbapi.connect(INSTANCE_ID, DATABASE_NAME) @@ -232,6 +231,7 @@ def run(self): self._cleanup() return measures + @pytest.mark.django_db() class BenchmarkTest(unittest.TestCase): def setUp(self): @@ -239,21 +239,52 @@ def setUp(self): setup_database() def test_run(self): - django_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', - 'insert_many_rows_with_mutations']) - spanner_obj = pd.DataFrame(columns = ['insert_one_row_with_fetch_after', 'read_one_row', 'insert_many_rows', 'select_many_rows', - 'insert_many_rows_with_mutations']) + django_obj = pd.DataFrame( + columns=[ + "insert_one_row_with_fetch_after", + "read_one_row", + "insert_many_rows", + "select_many_rows", + "insert_many_rows_with_mutations", + ] + ) + spanner_obj = pd.DataFrame( + columns=[ + "insert_one_row_with_fetch_after", + "read_one_row", + "insert_many_rows", + "select_many_rows", + "insert_many_rows_with_mutations", + ] + ) for _ in range(50): - django_obj = django_obj.append(DjangoBenchmarkTest().run(), ignore_index=True) - spanner_obj = spanner_obj.append(SpannerBenchmarkTest().run(), ignore_index=True) - - avg = pd.concat([django_obj.mean(axis = 0), spanner_obj.mean(axis = 0)], axis=1) - avg.columns=['Django','Spanner'] - std = pd.concat([django_obj.std(axis = 0), spanner_obj.std(axis = 0)], axis=1) - std.columns=['Django','Spanner'] - err = pd.concat([django_obj.sem(axis = 0), spanner_obj.sem(axis = 0)], axis=1) - err.columns=['Django','Spanner'] - - print("Average: ", avg, "Standard Deviation: ", std, "Error:", err, sep='\n') - + django_obj = django_obj.append( + DjangoBenchmarkTest().run(), ignore_index=True + ) + spanner_obj = spanner_obj.append( + SpannerBenchmarkTest().run(), ignore_index=True + ) + + avg = pd.concat( + [django_obj.mean(axis=0), spanner_obj.mean(axis=0)], axis=1 + ) + avg.columns = ["Django", "Spanner"] + std = pd.concat( + [django_obj.std(axis=0), spanner_obj.std(axis=0)], axis=1 + ) + std.columns = ["Django", "Spanner"] + err = pd.concat( + [django_obj.sem(axis=0), spanner_obj.sem(axis=0)], axis=1 + ) + err.columns = ["Django", "Spanner"] + + print( + "Average: ", + avg, + "Standard Deviation: ", + std, + "Error:", + err, + sep="\n", + ) From 488035c0989595c09f7215c5a03ec6fd42555bda Mon Sep 17 00:00:00 2001 From: asthamohta Date: Wed, 21 Jul 2021 15:09:47 +0530 Subject: [PATCH 08/10] changes for 3.2 --- django_spanner/introspection.py | 1 + django_spanner/utils.py | 14 +++++------ noxfile.py | 8 +++--- tests/unit/django_spanner/test_expressions.py | 4 +-- tests/unit/django_spanner/test_functions.py | 2 +- .../unit/django_spanner/test_introspection.py | 2 ++ tests/unit/django_spanner/test_lookups.py | 25 +++++++++---------- tests/unit/django_spanner/test_utils.py | 4 +-- 8 files changed, 31 insertions(+), 29 deletions(-) diff --git a/django_spanner/introspection.py b/django_spanner/introspection.py index b95ea3e629..f81a3ea953 100644 --- a/django_spanner/introspection.py +++ b/django_spanner/introspection.py @@ -96,6 +96,7 @@ def get_table_description(self, cursor, table_name): None, # scale details.null_ok, None, # default + None, # collation ) ) diff --git a/django_spanner/utils.py b/django_spanner/utils.py index 6fb40db812..de7a91cb05 100644 --- a/django_spanner/utils.py +++ b/django_spanner/utils.py @@ -18,13 +18,13 @@ def check_django_compatability(): """ from . import __version__ - if django.VERSION[:2] != get_version_tuple(__version__)[:2]: - raise ImproperlyConfigured( - "You must use the latest version of django-spanner {A}.{B}.x " - "with Django {A}.{B}.y (found django-spanner {C}).".format( - A=django.VERSION[0], B=django.VERSION[1], C=__version__ - ) - ) + # if django.VERSION[:2] != get_version_tuple(__version__)[:2]: + # raise ImproperlyConfigured( + # "You must use the latest version of django-spanner {A}.{B}.x " + # "with Django {A}.{B}.y (found django-spanner {C}).".format( + # A=django.VERSION[0], B=django.VERSION[1], C=__version__ + # ) + # ) def add_dummy_where(sql): diff --git a/noxfile.py b/noxfile.py index a5c05e7a02..bcfe118ca3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -69,7 +69,7 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. session.install( - "django~=2.2", + "django~=3.2", "mock", "mock-import", "pytest", @@ -136,7 +136,7 @@ def system(session): # Install all test dependencies, then install this package into the # virtualenv's dist-packages. session.install( - "django~=2.2", + "django~=3.2", "mock", "pytest", "google-cloud-testutils", @@ -172,7 +172,7 @@ def docs(session): """Build the docs for this library.""" session.install("-e", ".[tracing]") - session.install("sphinx", "alabaster", "recommonmark", "django==2.2") + session.install("sphinx", "alabaster", "recommonmark", "django==3.2") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) # Warnings as errors is disabled for `sphinx-build` because django module @@ -200,7 +200,7 @@ def docfx(session): "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml", - "django==2.2", + "django==3.2", ) shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) diff --git a/tests/unit/django_spanner/test_expressions.py b/tests/unit/django_spanner/test_expressions.py index 0efc99ce08..ae5b53f4f8 100644 --- a/tests/unit/django_spanner/test_expressions.py +++ b/tests/unit/django_spanner/test_expressions.py @@ -20,7 +20,7 @@ def test_order_by_sql_query_with_order_by_null_last(self): self.assertEqual( sql_compiled, "SELECT tests_report.name FROM tests_report ORDER BY " - + "tests_report.name IS NULL, tests_report.name DESC", + + "tests_report.name IS NULL, tests_report.name DESC NULLS LAST", ) def test_order_by_sql_query_with_order_by_null_first(self): @@ -32,7 +32,7 @@ def test_order_by_sql_query_with_order_by_null_first(self): self.assertEqual( sql_compiled, "SELECT tests_report.name FROM tests_report ORDER BY " - + "tests_report.name IS NOT NULL, tests_report.name DESC", + + "tests_report.name IS NOT NULL, tests_report.name DESC NULLS FIRST", ) def test_order_by_sql_query_with_order_by_name(self): diff --git a/tests/unit/django_spanner/test_functions.py b/tests/unit/django_spanner/test_functions.py index 00b431b73b..b24a2290e9 100644 --- a/tests/unit/django_spanner/test_functions.py +++ b/tests/unit/django_spanner/test_functions.py @@ -179,7 +179,7 @@ def test_pi(self): self.assertEqual( sql_query, "SELECT tests_author.num FROM tests_author WHERE tests_author.num " - + "= (3.141592653589793)", + + "= 3.141592653589793", ) self.assertEqual(params, ()) diff --git a/tests/unit/django_spanner/test_introspection.py b/tests/unit/django_spanner/test_introspection.py index c90288f3b3..03b5b67ca9 100644 --- a/tests/unit/django_spanner/test_introspection.py +++ b/tests/unit/django_spanner/test_introspection.py @@ -98,6 +98,7 @@ def get_table_column_schema(*args, **kwargs): scale=None, null_ok=False, default=None, + collation=None, ), FieldInfo( name="age", @@ -108,6 +109,7 @@ def get_table_column_schema(*args, **kwargs): scale=None, null_ok=True, default=None, + collation=None, ), ], ) diff --git a/tests/unit/django_spanner/test_lookups.py b/tests/unit/django_spanner/test_lookups.py index 53604691cc..797dfbaba8 100644 --- a/tests/unit/django_spanner/test_lookups.py +++ b/tests/unit/django_spanner/test_lookups.py @@ -10,7 +10,6 @@ from decimal import Decimal from .models import Number, Author - class TestLookups(SpannerSimpleTestClass): def test_cast_param_to_float_lte_sql_query(self): @@ -59,7 +58,7 @@ def test_cast_param_to_float_with_no_params_query(self): self.assertEqual( sql_compiled, "SELECT tests_number.num FROM tests_number WHERE " - + "tests_number.item_id = (tests_number.num)", + + "tests_number.item_id = tests_number.num", ) self.assertEqual(params, ()) @@ -111,8 +110,8 @@ def test_startswith_endswith_sql_query_with_bileteral_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT('^', (UPPER(%s))), " - + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', + + "REPLACE(REPLACE(REPLACE(CONCAT(\'^\', UPPER(%s)), " + +'"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -128,7 +127,7 @@ def test_startswith_endswith_case_insensitive_transform_sql_query(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT('^(?i)', (UPPER(%s))), " + + "REPLACE(REPLACE(REPLACE(CONCAT(\'^(?i)\', UPPER(%s)), " + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -144,7 +143,7 @@ def test_startswith_endswith_endswith_sql_query_with_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT('', (UPPER(%s)), '$'), " + + "REPLACE(REPLACE(REPLACE(CONCAT(\'\', UPPER(%s), \'$\'), " + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -183,7 +182,7 @@ def test_regex_sql_query_case_sensitive_with_transform(self): sql_compiled, "SELECT tests_author.num FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "(UPPER(%s)))", + + "UPPER(%s))", ) self.assertEqual(params, ("abc",)) @@ -197,7 +196,7 @@ def test_regex_sql_query_case_insensitive_with_transform(self): sql_compiled, "SELECT tests_author.num FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "CONCAT('(?i)', (UPPER(%s))))", + + "CONCAT('(?i)', UPPER(%s)))", ) self.assertEqual(params, ("abc",)) @@ -236,7 +235,7 @@ def test_contains_sql_query_case_insensitive_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT('(?i)', (UPPER(%s))), " + + "REPLACE(REPLACE(REPLACE(CONCAT(\'(?i)\', UPPER(%s)), " + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -250,7 +249,7 @@ def test_contains_sql_query_case_sensitive_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + 'REPLACE(REPLACE(REPLACE((UPPER(%s)), "\\\\", "\\\\\\\\"), ' + + 'REPLACE(REPLACE(REPLACE(UPPER(%s), "\\\\", "\\\\\\\\"), ' + '"%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -279,7 +278,7 @@ def test_iexact_sql_query_case_insensitive_function_transform(self): self.assertEqual( sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " - + "REGEXP_CONTAINS((UPPER(tests_author.last_name)), " + + "REGEXP_CONTAINS(UPPER(tests_author.last_name), " + "CONCAT('^(?i)', CAST(UPPER(tests_author.name) AS STRING), '$'))", ) self.assertEqual(params, ()) @@ -293,7 +292,7 @@ def test_iexact_sql_query_case_insensitive_value_match(self): self.assertEqual( sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " - + "REGEXP_CONTAINS((UPPER(CONCAT('^(?i)', " - + "CAST(UPPER(tests_author.name) AS STRING), '$'))), %s)", + + "REGEXP_CONTAINS(UPPER(CONCAT('^(?i)', " + + "CAST(UPPER(tests_author.name) AS STRING), '$')), %s)", ) self.assertEqual(params, ("abc",)) diff --git a/tests/unit/django_spanner/test_utils.py b/tests/unit/django_spanner/test_utils.py index e4d50861d0..b8c353ab2d 100644 --- a/tests/unit/django_spanner/test_utils.py +++ b/tests/unit/django_spanner/test_utils.py @@ -20,7 +20,7 @@ def test_check_django_compatability_match(self): """ Checks django compatibility match. """ - django_spanner.__version__ = "2.2" + django_spanner.__version__ = "3.2" django.VERSION = (2, 2, 19, "alpha", 0) check_django_compatability() @@ -28,7 +28,7 @@ def test_check_django_compatability_mismatch(self): """ Checks django compatibility mismatch. """ - django_spanner.__version__ = "2.2" + django_spanner.__version__ = "3.2" django.VERSION = (3, 2, 19, "alpha", 0) with self.assertRaises(ImproperlyConfigured): check_django_compatability() From e5b8399de7af2a1b99c25268254c76775cb5114f Mon Sep 17 00:00:00 2001 From: asthamohta Date: Wed, 21 Jul 2021 15:10:52 +0530 Subject: [PATCH 09/10] Revert "changes for 3.2" This reverts commit 488035c0989595c09f7215c5a03ec6fd42555bda. --- django_spanner/introspection.py | 1 - django_spanner/utils.py | 14 +++++------ noxfile.py | 8 +++--- tests/unit/django_spanner/test_expressions.py | 4 +-- tests/unit/django_spanner/test_functions.py | 2 +- .../unit/django_spanner/test_introspection.py | 2 -- tests/unit/django_spanner/test_lookups.py | 25 ++++++++++--------- tests/unit/django_spanner/test_utils.py | 4 +-- 8 files changed, 29 insertions(+), 31 deletions(-) diff --git a/django_spanner/introspection.py b/django_spanner/introspection.py index f81a3ea953..b95ea3e629 100644 --- a/django_spanner/introspection.py +++ b/django_spanner/introspection.py @@ -96,7 +96,6 @@ def get_table_description(self, cursor, table_name): None, # scale details.null_ok, None, # default - None, # collation ) ) diff --git a/django_spanner/utils.py b/django_spanner/utils.py index de7a91cb05..6fb40db812 100644 --- a/django_spanner/utils.py +++ b/django_spanner/utils.py @@ -18,13 +18,13 @@ def check_django_compatability(): """ from . import __version__ - # if django.VERSION[:2] != get_version_tuple(__version__)[:2]: - # raise ImproperlyConfigured( - # "You must use the latest version of django-spanner {A}.{B}.x " - # "with Django {A}.{B}.y (found django-spanner {C}).".format( - # A=django.VERSION[0], B=django.VERSION[1], C=__version__ - # ) - # ) + if django.VERSION[:2] != get_version_tuple(__version__)[:2]: + raise ImproperlyConfigured( + "You must use the latest version of django-spanner {A}.{B}.x " + "with Django {A}.{B}.y (found django-spanner {C}).".format( + A=django.VERSION[0], B=django.VERSION[1], C=__version__ + ) + ) def add_dummy_where(sql): diff --git a/noxfile.py b/noxfile.py index bcfe118ca3..a5c05e7a02 100644 --- a/noxfile.py +++ b/noxfile.py @@ -69,7 +69,7 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. session.install( - "django~=3.2", + "django~=2.2", "mock", "mock-import", "pytest", @@ -136,7 +136,7 @@ def system(session): # Install all test dependencies, then install this package into the # virtualenv's dist-packages. session.install( - "django~=3.2", + "django~=2.2", "mock", "pytest", "google-cloud-testutils", @@ -172,7 +172,7 @@ def docs(session): """Build the docs for this library.""" session.install("-e", ".[tracing]") - session.install("sphinx", "alabaster", "recommonmark", "django==3.2") + session.install("sphinx", "alabaster", "recommonmark", "django==2.2") shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) # Warnings as errors is disabled for `sphinx-build` because django module @@ -200,7 +200,7 @@ def docfx(session): "alabaster", "recommonmark", "gcp-sphinx-docfx-yaml", - "django==3.2", + "django==2.2", ) shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) diff --git a/tests/unit/django_spanner/test_expressions.py b/tests/unit/django_spanner/test_expressions.py index ae5b53f4f8..0efc99ce08 100644 --- a/tests/unit/django_spanner/test_expressions.py +++ b/tests/unit/django_spanner/test_expressions.py @@ -20,7 +20,7 @@ def test_order_by_sql_query_with_order_by_null_last(self): self.assertEqual( sql_compiled, "SELECT tests_report.name FROM tests_report ORDER BY " - + "tests_report.name IS NULL, tests_report.name DESC NULLS LAST", + + "tests_report.name IS NULL, tests_report.name DESC", ) def test_order_by_sql_query_with_order_by_null_first(self): @@ -32,7 +32,7 @@ def test_order_by_sql_query_with_order_by_null_first(self): self.assertEqual( sql_compiled, "SELECT tests_report.name FROM tests_report ORDER BY " - + "tests_report.name IS NOT NULL, tests_report.name DESC NULLS FIRST", + + "tests_report.name IS NOT NULL, tests_report.name DESC", ) def test_order_by_sql_query_with_order_by_name(self): diff --git a/tests/unit/django_spanner/test_functions.py b/tests/unit/django_spanner/test_functions.py index b24a2290e9..00b431b73b 100644 --- a/tests/unit/django_spanner/test_functions.py +++ b/tests/unit/django_spanner/test_functions.py @@ -179,7 +179,7 @@ def test_pi(self): self.assertEqual( sql_query, "SELECT tests_author.num FROM tests_author WHERE tests_author.num " - + "= 3.141592653589793", + + "= (3.141592653589793)", ) self.assertEqual(params, ()) diff --git a/tests/unit/django_spanner/test_introspection.py b/tests/unit/django_spanner/test_introspection.py index 03b5b67ca9..c90288f3b3 100644 --- a/tests/unit/django_spanner/test_introspection.py +++ b/tests/unit/django_spanner/test_introspection.py @@ -98,7 +98,6 @@ def get_table_column_schema(*args, **kwargs): scale=None, null_ok=False, default=None, - collation=None, ), FieldInfo( name="age", @@ -109,7 +108,6 @@ def get_table_column_schema(*args, **kwargs): scale=None, null_ok=True, default=None, - collation=None, ), ], ) diff --git a/tests/unit/django_spanner/test_lookups.py b/tests/unit/django_spanner/test_lookups.py index 797dfbaba8..53604691cc 100644 --- a/tests/unit/django_spanner/test_lookups.py +++ b/tests/unit/django_spanner/test_lookups.py @@ -10,6 +10,7 @@ from decimal import Decimal from .models import Number, Author + class TestLookups(SpannerSimpleTestClass): def test_cast_param_to_float_lte_sql_query(self): @@ -58,7 +59,7 @@ def test_cast_param_to_float_with_no_params_query(self): self.assertEqual( sql_compiled, "SELECT tests_number.num FROM tests_number WHERE " - + "tests_number.item_id = tests_number.num", + + "tests_number.item_id = (tests_number.num)", ) self.assertEqual(params, ()) @@ -110,8 +111,8 @@ def test_startswith_endswith_sql_query_with_bileteral_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT(\'^\', UPPER(%s)), " - +'"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', + + "REPLACE(REPLACE(REPLACE(CONCAT('^', (UPPER(%s))), " + + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -127,7 +128,7 @@ def test_startswith_endswith_case_insensitive_transform_sql_query(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT(\'^(?i)\', UPPER(%s)), " + + "REPLACE(REPLACE(REPLACE(CONCAT('^(?i)', (UPPER(%s))), " + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -143,7 +144,7 @@ def test_startswith_endswith_endswith_sql_query_with_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT(\'\', UPPER(%s), \'$\'), " + + "REPLACE(REPLACE(REPLACE(CONCAT('', (UPPER(%s)), '$'), " + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -182,7 +183,7 @@ def test_regex_sql_query_case_sensitive_with_transform(self): sql_compiled, "SELECT tests_author.num FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "UPPER(%s))", + + "(UPPER(%s)))", ) self.assertEqual(params, ("abc",)) @@ -196,7 +197,7 @@ def test_regex_sql_query_case_insensitive_with_transform(self): sql_compiled, "SELECT tests_author.num FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "CONCAT('(?i)', UPPER(%s)))", + + "CONCAT('(?i)', (UPPER(%s))))", ) self.assertEqual(params, ("abc",)) @@ -235,7 +236,7 @@ def test_contains_sql_query_case_insensitive_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + "REPLACE(REPLACE(REPLACE(CONCAT(\'(?i)\', UPPER(%s)), " + + "REPLACE(REPLACE(REPLACE(CONCAT('(?i)', (UPPER(%s))), " + '"\\\\", "\\\\\\\\"), "%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -249,7 +250,7 @@ def test_contains_sql_query_case_sensitive_transform(self): sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " + "REGEXP_CONTAINS(CAST(UPPER(tests_author.name) AS STRING), " - + 'REPLACE(REPLACE(REPLACE(UPPER(%s), "\\\\", "\\\\\\\\"), ' + + 'REPLACE(REPLACE(REPLACE((UPPER(%s)), "\\\\", "\\\\\\\\"), ' + '"%%", r"\\%%"), "_", r"\\_"))', ) self.assertEqual(params, ("abc",)) @@ -278,7 +279,7 @@ def test_iexact_sql_query_case_insensitive_function_transform(self): self.assertEqual( sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " - + "REGEXP_CONTAINS(UPPER(tests_author.last_name), " + + "REGEXP_CONTAINS((UPPER(tests_author.last_name)), " + "CONCAT('^(?i)', CAST(UPPER(tests_author.name) AS STRING), '$'))", ) self.assertEqual(params, ()) @@ -292,7 +293,7 @@ def test_iexact_sql_query_case_insensitive_value_match(self): self.assertEqual( sql_compiled, "SELECT tests_author.name FROM tests_author WHERE " - + "REGEXP_CONTAINS(UPPER(CONCAT('^(?i)', " - + "CAST(UPPER(tests_author.name) AS STRING), '$')), %s)", + + "REGEXP_CONTAINS((UPPER(CONCAT('^(?i)', " + + "CAST(UPPER(tests_author.name) AS STRING), '$'))), %s)", ) self.assertEqual(params, ("abc",)) diff --git a/tests/unit/django_spanner/test_utils.py b/tests/unit/django_spanner/test_utils.py index b8c353ab2d..e4d50861d0 100644 --- a/tests/unit/django_spanner/test_utils.py +++ b/tests/unit/django_spanner/test_utils.py @@ -20,7 +20,7 @@ def test_check_django_compatability_match(self): """ Checks django compatibility match. """ - django_spanner.__version__ = "3.2" + django_spanner.__version__ = "2.2" django.VERSION = (2, 2, 19, "alpha", 0) check_django_compatability() @@ -28,7 +28,7 @@ def test_check_django_compatability_mismatch(self): """ Checks django compatibility mismatch. """ - django_spanner.__version__ = "3.2" + django_spanner.__version__ = "2.2" django.VERSION = (3, 2, 19, "alpha", 0) with self.assertRaises(ImproperlyConfigured): check_django_compatability() From fa6c32b661488647b4824fabad7a61266c81736f Mon Sep 17 00:00:00 2001 From: asthamohta Date: Wed, 21 Jul 2021 15:47:45 +0530 Subject: [PATCH 10/10] adding licence --- tests/performance/__init__.py | 2 +- tests/performance/django_spanner/__init__.py | 5 +++++ tests/performance/django_spanner/models.py | 6 ++++++ tests/performance/django_spanner/test_benchmark.py | 6 ++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/performance/__init__.py b/tests/performance/__init__.py index 6b607710ed..529352b757 100644 --- a/tests/performance/__init__.py +++ b/tests/performance/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2020 Google LLC +# Copyright 2021 Google LLC # # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file or at diff --git a/tests/performance/django_spanner/__init__.py b/tests/performance/django_spanner/__init__.py index e69de29bb2..529352b757 100644 --- a/tests/performance/django_spanner/__init__.py +++ b/tests/performance/django_spanner/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd diff --git a/tests/performance/django_spanner/models.py b/tests/performance/django_spanner/models.py index 83a6bd8273..d387a6ef86 100644 --- a/tests/performance/django_spanner/models.py +++ b/tests/performance/django_spanner/models.py @@ -1,3 +1,9 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + from django.db import models diff --git a/tests/performance/django_spanner/test_benchmark.py b/tests/performance/django_spanner/test_benchmark.py index 1d86e87c48..31dd8f2987 100644 --- a/tests/performance/django_spanner/test_benchmark.py +++ b/tests/performance/django_spanner/test_benchmark.py @@ -1,3 +1,9 @@ +# Copyright 2021 Google LLC +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + import random import time import unittest