From 4bbad15325245a95aa5ccd341b57a3150b6eb2db Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Tue, 4 Apr 2017 13:44:33 -0700 Subject: [PATCH 1/5] Updated the version of spanner library and changed the samples to work with that. --- spanner/crud.js | 25 ++++--- spanner/package.json | 2 +- spanner/transaction.js | 146 +++++++++++++++++++---------------------- 3 files changed, 79 insertions(+), 94 deletions(-) diff --git a/spanner/crud.js b/spanner/crud.js index 761e22744c..cb258ca170 100644 --- a/spanner/crud.js +++ b/spanner/crud.js @@ -66,18 +66,17 @@ function insertData (instanceId, databaseId) { const singersTable = database.table('Singers'); const albumsTable = database.table('Albums'); - Promise.all([ - // Inserts rows into the Singers table - // Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so - // they must be converted to strings before being inserted as INT64s - singersTable.insert([ - { SingerId: '1', FirstName: 'Marc', LastName: 'Richards' }, - { SingerId: '2', FirstName: 'Catalina', LastName: 'Smith' }, - { SingerId: '3', FirstName: 'Alice', LastName: 'Trentor' }, - { SingerId: '4', FirstName: 'Lea', LastName: 'Martin' }, - { SingerId: '5', FirstName: 'David', LastName: 'Lomond' } - ]), - + // Inserts rows into the Singers table + // Note: Cloud Spanner interprets Node.js numbers as FLOAT64s, so + // they must be converted to strings before being inserted as INT64s + singersTable.insert([ + { SingerId: '1', FirstName: 'Marc', LastName: 'Richards' }, + { SingerId: '2', FirstName: 'Catalina', LastName: 'Smith' }, + { SingerId: '3', FirstName: 'Alice', LastName: 'Trentor' }, + { SingerId: '4', FirstName: 'Lea', LastName: 'Martin' }, + { SingerId: '5', FirstName: 'David', LastName: 'Lomond' } + ]) + .then(() => { // Inserts rows into the Albums table albumsTable.insert([ { SingerId: '1', AlbumId: '1', AlbumTitle: 'Go, Go, Go' }, @@ -86,7 +85,7 @@ function insertData (instanceId, databaseId) { { SingerId: '2', AlbumId: '2', AlbumTitle: 'Forever Hold your Peace' }, { SingerId: '2', AlbumId: '3', AlbumTitle: 'Terrified' } ]) - ]) + }) .then(() => { console.log('Inserted data.'); }); diff --git a/spanner/package.json b/spanner/package.json index 0a06cb93cd..5af224b38b 100644 --- a/spanner/package.json +++ b/spanner/package.json @@ -8,7 +8,7 @@ "test": "cd ..; npm run st -- --verbose spanner/system-test/*.test.js" }, "dependencies": { - "@google-cloud/spanner": "0.1.0", + "@google-cloud/spanner": "0.3.0", "yargs": "6.6.0" }, "engines": { diff --git a/spanner/transaction.js b/spanner/transaction.js index d1a6f06501..0eb788d2d1 100644 --- a/spanner/transaction.js +++ b/spanner/transaction.js @@ -33,46 +33,37 @@ function readOnlyTransaction (instanceId, databaseId) { // Gets a transaction object that captures the database state // at a specific point in time - database.runTransaction({readOnly: true}) - .then((results) => { - const transaction = results[0]; + database.runTransaction({readOnly: true}, function(err, transaction) { const queryOne = 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums'; // Read #1, using SQL - transaction.run(queryOne) - .then((results) => { - const rows = results[0]; - + transaction.run(queryOne, function(err, rows) { rows.forEach((row) => { const json = row.toJSON(); console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); }); - }); - - const queryTwo = { - columns: ['SingerId', 'AlbumId', 'AlbumTitle'], - keySet: { - all: true - } - }; - - // Read #2, using the `read` method. Even if changes occur - // in-between the reads, the transaction ensures that both - // return the same data. - transaction.read('Albums', queryTwo) - .then((results) => { - const rows = results[0]; - rows.forEach((row) => { - const json = row.toJSON(); - console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); + const queryTwo = { + columns: ['SingerId', 'AlbumId', 'AlbumTitle'], + keySet: { + all: true + } + }; + + // Read #2, using the `read` method. Even if changes occur + // in-between the reads, the transaction ensures that both + // return the same data. + transaction.read('Albums', queryTwo, function(err, rows) { + rows.forEach((row) => { + const json = row.toJSON(); + console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); + }); + console.log('Successfully executed read-only transaction.'); + transaction.end(); }); - }); - }) - .then(() => { - console.log('Successfully executed read-only transaction.'); - }); + }); + }); // [END read_only_transaction] } @@ -96,53 +87,47 @@ function readWriteTransaction (instanceId, databaseId) { const instance = spanner.instance(instanceId); const database = instance.database(databaseId); - // Gets a transaction object that captures the database state - // at a specific point in time - let transaction, firstBudget, secondBudget; const transferAmount = 200000; const minimumAmountToTransfer = 300000; - database.runTransaction() - .then((results) => { - transaction = results[0]; - - const queryOne = { - columns: [`MarketingBudget`], - keys: [2, 2] // SingerId: 2, AlbumId: 2 - }; - - const queryTwo = { - columns: ['MarketingBudget'], - keys: [1, 1] // SingerId: 1, AlbumId: 1 - }; - - return Promise.all([ - // Reads the second album's budget - transaction.read('Albums', queryOne).then((results) => { - // Gets second album's budget - // Note: MarketingBudget is an INT64, which comes from Cloud Spanner - // as a string - so we convert it to a number with parseInt() - const rows = results[0].map((row) => row.toJSON()); - secondBudget = parseInt(rows[0].MarketingBudget.value); - console.log(`The second album's marketing budget: ${secondBudget}`); - - // Makes sure the second album's budget is sufficient - if (secondBudget < minimumAmountToTransfer) { - throw new Error(`The second album's budget (${secondBudget}) is less than the minimum required amount to transfer.`); - } - }), - - // Reads the first album's budget - transaction.read('Albums', queryTwo).then((results) => { - // Gets first album's budget - // As above, MarketingBudget is an INT64 and comes as a string - const rows = results[0].map((row) => row.toJSON()); - firstBudget = parseInt(rows[0].MarketingBudget.value); - console.log(`The first album's marketing budget: ${firstBudget}`); - }) - ]); - }) - .then(() => { + database.runTransaction(function(err, transaction) { + + let firstBudget, secondBudget; + const queryOne = { + columns: [`MarketingBudget`], + keys: [[2, 2]] // SingerId: 2, AlbumId: 2 + }; + + const queryTwo = { + columns: ['MarketingBudget'], + keys: [[1, 1]] // SingerId: 1, AlbumId: 1 + }; + + Promise.all([ + // Reads the second album's budget + transaction.read('Albums', queryOne).then((results) => { + // Gets second album's budget + // Note: MarketingBudget is an INT64, which comes from Cloud Spanner + // as a string - so we convert it to a number with parseInt() + const rows = results[0].map((row) => row.toJSON()); + secondBudget = parseInt(rows[0].MarketingBudget.value); + console.log(`The second album's marketing budget: ${secondBudget}`); + + // Makes sure the second album's budget is sufficient + if (secondBudget < minimumAmountToTransfer) { + throw new Error(`The second album's budget (${secondBudget}) is less than the minimum required amount to transfer.`); + } + }), + + // Reads the first album's budget + transaction.read('Albums', queryTwo).then((results) => { + // Gets first album's budget + // As above, MarketingBudget is an INT64 and comes as a string + const rows = results[0].map((row) => row.toJSON()); + firstBudget = parseInt(rows[0].MarketingBudget.value); + console.log(`The first album's marketing budget: ${firstBudget}`); + }) + ]).then(() => { // Transfer the budgets between the albums console.log(firstBudget, secondBudget); firstBudget += transferAmount; @@ -159,12 +144,13 @@ function readWriteTransaction (instanceId, databaseId) { ]); }) // Commits the transaction and send the changes to the database - .then(() => transaction.commit()) - .then(() => { - // Logs success - console.log(`Successfully executed read-write transaction to transfer ${transferAmount} from Album 2 to Album 1.`); - }); - // [END read_write_transaction] + .then(() => transaction.commit(function(err) { + if (!err) { + console.log(`Successfully executed read-write transaction to transfer ${transferAmount} from Album 2 to Album 1.`); + } + })) + }); + // [END read_write_transaction] } const cli = require(`yargs`) From 43d28a435e71f1080d0f3279aa68e9958a91c50e Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Tue, 4 Apr 2017 13:56:23 -0700 Subject: [PATCH 2/5] Fixed some lint errors --- spanner/crud.js | 2 +- spanner/transaction.js | 78 ++++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/spanner/crud.js b/spanner/crud.js index cb258ca170..7c8834571d 100644 --- a/spanner/crud.js +++ b/spanner/crud.js @@ -84,7 +84,7 @@ function insertData (instanceId, databaseId) { { SingerId: '2', AlbumId: '1', AlbumTitle: 'Green' }, { SingerId: '2', AlbumId: '2', AlbumTitle: 'Forever Hold your Peace' }, { SingerId: '2', AlbumId: '3', AlbumTitle: 'Terrified' } - ]) + ]); }) .then(() => { console.log('Inserted data.'); diff --git a/spanner/transaction.js b/spanner/transaction.js index 0eb788d2d1..fc3f3590ee 100644 --- a/spanner/transaction.js +++ b/spanner/transaction.js @@ -33,36 +33,44 @@ function readOnlyTransaction (instanceId, databaseId) { // Gets a transaction object that captures the database state // at a specific point in time - database.runTransaction({readOnly: true}, function(err, transaction) { - - const queryOne = 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums'; + database.runTransaction({readOnly: true}, function (err, transaction) { + if (err) { + // Error handling omitted. + } + const queryOne = 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums'; // Read #1, using SQL - transaction.run(queryOne, function(err, rows) { - rows.forEach((row) => { - const json = row.toJSON(); - console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); - }); - - const queryTwo = { - columns: ['SingerId', 'AlbumId', 'AlbumTitle'], - keySet: { - all: true - } - }; - - // Read #2, using the `read` method. Even if changes occur - // in-between the reads, the transaction ensures that both - // return the same data. - transaction.read('Albums', queryTwo, function(err, rows) { - rows.forEach((row) => { - const json = row.toJSON(); - console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); - }); - console.log('Successfully executed read-only transaction.'); - transaction.end(); - }); + transaction.run(queryOne, function (err, rows) { + if (err) { + // Error handling omitted. + } + rows.forEach((row) => { + const json = row.toJSON(); + console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); + }); + + const queryTwo = { + columns: ['SingerId', 'AlbumId', 'AlbumTitle'], + keySet: { + all: true + } + }; + + // Read #2, using the `read` method. Even if changes occur + // in-between the reads, the transaction ensures that both + // return the same data. + transaction.read('Albums', queryTwo, function (err, rows) { + if (err) { + // Error handling omitted. + } + rows.forEach((row) => { + const json = row.toJSON(); + console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); + }); + console.log('Successfully executed read-only transaction.'); + transaction.end(); }); + }); }); // [END read_only_transaction] } @@ -90,8 +98,10 @@ function readWriteTransaction (instanceId, databaseId) { const transferAmount = 200000; const minimumAmountToTransfer = 300000; - database.runTransaction(function(err, transaction) { - + database.runTransaction(function (err, transaction) { + if (err) { + // Error handling omitted. + } let firstBudget, secondBudget; const queryOne = { columns: [`MarketingBudget`], @@ -144,11 +154,11 @@ function readWriteTransaction (instanceId, databaseId) { ]); }) // Commits the transaction and send the changes to the database - .then(() => transaction.commit(function(err) { - if (!err) { - console.log(`Successfully executed read-write transaction to transfer ${transferAmount} from Album 2 to Album 1.`); - } - })) + .then(() => transaction.commit(function (err) { + if (!err) { + console.log(`Successfully executed read-write transaction to transfer ${transferAmount} from Album 2 to Album 1.`); + } + })); }); // [END read_write_transaction] } From 026b2cb629b4ca5e5a02f49ea50a802d98fb42a2 Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Tue, 4 Apr 2017 15:01:26 -0700 Subject: [PATCH 3/5] Converted callbacks to promises wherever possible --- spanner/transaction.js | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/spanner/transaction.js b/spanner/transaction.js index fc3f3590ee..69ee4b091c 100644 --- a/spanner/transaction.js +++ b/spanner/transaction.js @@ -33,36 +33,34 @@ function readOnlyTransaction (instanceId, databaseId) { // Gets a transaction object that captures the database state // at a specific point in time - database.runTransaction({readOnly: true}, function (err, transaction) { + database.runTransaction({readOnly: true}, (err, transaction) => { if (err) { // Error handling omitted. } const queryOne = 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums'; // Read #1, using SQL - transaction.run(queryOne, function (err, rows) { - if (err) { - // Error handling omitted. - } - rows.forEach((row) => { - const json = row.toJSON(); - console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); - }); - - const queryTwo = { - columns: ['SingerId', 'AlbumId', 'AlbumTitle'], - keySet: { - all: true - } - }; + transaction.run(queryOne) + .then((results) => { + const rows = results[0]; + rows.forEach((row) => { + const json = row.toJSON(); + console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); + }); + const queryTwo = { + columns: ['SingerId', 'AlbumId', 'AlbumTitle'], + keySet: { + all: true + } + }; // Read #2, using the `read` method. Even if changes occur // in-between the reads, the transaction ensures that both // return the same data. - transaction.read('Albums', queryTwo, function (err, rows) { - if (err) { - // Error handling omitted. - } + return transaction.read('Albums', queryTwo); + }) + .then((results) => { + const rows = results[0]; rows.forEach((row) => { const json = row.toJSON(); console.log(`SingerId: ${json.SingerId.value}, AlbumId: ${json.AlbumId.value}, AlbumTitle: ${json.AlbumTitle}`); @@ -70,7 +68,6 @@ function readOnlyTransaction (instanceId, databaseId) { console.log('Successfully executed read-only transaction.'); transaction.end(); }); - }); }); // [END read_only_transaction] } From cbf5f43d92b10f73144a23ae60a5374a7dcb7918 Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 5 Apr 2017 15:07:04 -0700 Subject: [PATCH 4/5] Fixed error handling --- spanner/transaction.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/spanner/transaction.js b/spanner/transaction.js index 69ee4b091c..bc6d03eb32 100644 --- a/spanner/transaction.js +++ b/spanner/transaction.js @@ -35,7 +35,8 @@ function readOnlyTransaction (instanceId, databaseId) { // at a specific point in time database.runTransaction({readOnly: true}, (err, transaction) => { if (err) { - // Error handling omitted. + console.error(err); + return; } const queryOne = 'SELECT SingerId, AlbumId, AlbumTitle FROM Albums'; @@ -97,7 +98,8 @@ function readWriteTransaction (instanceId, databaseId) { database.runTransaction(function (err, transaction) { if (err) { - // Error handling omitted. + console.error(err); + return; } let firstBudget, secondBudget; const queryOne = { @@ -152,7 +154,9 @@ function readWriteTransaction (instanceId, databaseId) { }) // Commits the transaction and send the changes to the database .then(() => transaction.commit(function (err) { - if (!err) { + if (err) { + console.error(err); + } else { console.log(`Successfully executed read-write transaction to transfer ${transferAmount} from Album 2 to Album 1.`); } })); From b698735da999d5526ca550f49d95f966d982e3c4 Mon Sep 17 00:00:00 2001 From: Vikas Kedia Date: Wed, 5 Apr 2017 15:45:05 -0700 Subject: [PATCH 5/5] Used arrow functions --- spanner/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spanner/transaction.js b/spanner/transaction.js index bc6d03eb32..a11059d9b7 100644 --- a/spanner/transaction.js +++ b/spanner/transaction.js @@ -96,7 +96,7 @@ function readWriteTransaction (instanceId, databaseId) { const transferAmount = 200000; const minimumAmountToTransfer = 300000; - database.runTransaction(function (err, transaction) { + database.runTransaction((err, transaction) => { if (err) { console.error(err); return; @@ -153,7 +153,7 @@ function readWriteTransaction (instanceId, databaseId) { ]); }) // Commits the transaction and send the changes to the database - .then(() => transaction.commit(function (err) { + .then(() => transaction.commit((err) => { if (err) { console.error(err); } else {