Laptop

Learn How To Stream JSON Data Into BigQuery Using The New BigQuery Storage Write API

The Google BigQuery Write API offers high-performance batching and streaming in one unified API. The previous post in this series introduced the BigQuery Write API. In this post, we’ll show how to stream JSON data to BigQuery by using the Java client library.

The Write API expects binary data in protocol buffer format. This makes the API very efficient for high-throughput streaming. However, protocol buffers can also be somewhat difficult to work with. For many applications, JSON data is a more convenient data format. The BigQuery client library for Java provides the best of both worlds, through the JsonStreamWriter. The JsonStreamWriter accepts data in the form of JSON records, and automatically converts the JSON objects into binary protocol buffers before sending them over the wire.

Let’s see how it works.


The scenario

Let’s consider a scenario where you are streaming GitHub commit data to BigQuery. You can use this data to get real-time insights about the commit activity.

For the purpose of the example, we’ll read the data from a local file. However, you can imagine an application that receives this data in the form of events or streamed from a log file. In fact, one advantage of the Write API is that it doesn’t matter where the data comes from, as long as you can get it into a compatible format.

Each line in the source file has the following structure:

{
    "commit":"commit ID",
    "parent":["parent commit ID"],
    "author":"author",
    "committer":"committer",
    "commit_date":"YYYY-MM-DDTHH:MM:SS"
    "commit_msg": {
        "subject":"Commit subject",
        "message":"Commit message", 
    }
    "repo_name":"Repo name"
}

Create the destination table

First, we need to create a table in BigQuery to receive the streamed data. There are several ways to create a table in BigQuery, but one of the easiest is by running a CREATE TABLE query:

CREATE TABLE `myproject.mydataset.github`
(
  commit STRING,
  parent ARRAY<STRING>,
  author STRING,
  committer STRING,
  commit_date DATETIME,
  commit_msg STRUCT<subject STRING, message STRING>,
  repo_name STRING
);

Stream data to the table

Now that we have a table, we can write data to it.

The Write API supports several modes, including committed mode for streaming applications that require exactly-once delivery, and pending mode for batch writes with stream-level transactions. For this example, we’ll use the Write API’s default stream. The default stream is suitable for streaming applications that require at-least-once semantics but don’t need the stronger exactly-one guarantee. Git commits have a unique commit ID, so you can identify duplication in the destination table if needed.

Start by initializing the JsonStreamWriter, passing in the name of the destination table and the table schema:

BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

    // Get the schema of the destination table and convert to the equivalent BigQueryStorage type.
    Table table = bigquery.getTable(datasetName, tableName);
    Schema schema = table.getDefinition().getSchema();
    TableSchema tableSchema = BqToBqStorageSchemaConverter.convertTableSchema(schema);

    // Use the JSON stream writer to send records in JSON format.
    TableName parentTable = TableName.of(projectId, datasetName, tableName);
    try (JsonStreamWriter writer =
        JsonStreamWriter.newBuilder(parentTable.toString(), tableSchema).build()) {

Now we’re ready to read the data file and send the data to the Write API. As a best practice, you should send the data in batches rather than one row at a time. Read each JSON record into a JSONObject and collect a batch of them into a JSONArray:

BufferedReader reader = new BufferedReader(new FileReader(dataFile));
      String line = reader.readLine();
      while (line != null) {
        JSONArray jsonArr = new JSONArray();
        for (int i = 0; i < 100; i++) {
          JSONObject record = new JSONObject(line);
          jsonArr.put(record);
          line = reader.readLine();
          if (line == null) {
            break;
          }
        } // batch

To write each batch of records, call the JsonStreamWriter.append method. This method is asynchronous and returns an ApiFuture. For best performance, don’t block waiting for the future to complete. Instead, continue to call append and handle the result asynchronously.

ApiFuture<AppendRowsResponse> future = writer.append(jsonArr);
        ApiFutures.addCallback(
            future, new AppendCompleteCallback(), MoreExecutors.directExecutor());

This example registers a completion callback. Inside the callback, you can check whether the append succeeded:

class AppendCompleteCallback implements ApiFutureCallback<AppendRowsResponse> {

  private static int batchCount = 0;
  private static final Object lock = new Object();

  public void onSuccess(AppendRowsResponse response) {
    synchronized (lock) {
      if (response.hasError()) {
        System.out.format("Error: %s\n", response.getError().toString());
      } else {
        ++batchCount;
        System.out.format("Wrote batch %d\n", batchCount);
      }
    }
  }

  public void onFailure(Throwable throwable) {
    System.out.format("Error: %s\n", throwable.toString());
  }
}

Handling table schema updates

BigQuery lets you modify the schema of an existing table in certain constrained ways. For example, you might decide to add a field named email that contains the commit author’s email. Data in the original schema will have a NULL value for this field. 

If the schema changes while you are streaming data, the Java client library automatically reconnects with the updated schema. In our example scenario, the application passes the data directly to the Write API without any intermediate processing. As long as the schema change is backward compatible, the application can continue streaming without interruption. After the table schema is updated, you can start sending data with the new field.

Note: Schema updates aren’t immediately visible to the client library, but are detected on the order of minutes.

To check for schema changes programmatically, call AppendRowsResponse.hasUpdatedSchema after the append method completes. For more information, see Working with Schemas in the Write API documentation.

Query the data

Our data is available for analysis as soon as it’s ingested into BigQuery. For example, we can now run a query to find out which days of the week have the most commits:

SELECT
  EXTRACT(DAYOFWEEK FROM ts) AS day_of_week,
  FORMAT_DATETIME("%A",commit_date) AS day,
  COUNT(*) AS count
FROM `mydataset.github`
GROUP BY day_of_week, day
ORDER BY count DESC;

+-------------+-----------+-------+
| day_of_week |    day    | count |
+-------------+-----------+-------+
|           3 | Tuesday   |   283 |
|           4 | Wednesday |   281 |
|           5 | Thursday  |   263 |
|           2 | Monday    |   201 |
|           6 | Friday    |   201 |
|           7 | Saturday  |    39 |
|           1 | Sunday    |    31 |
+-------------+-----------+-------+

It turns out that the busiest day is Tuesday. Not surprisingly, Saturday and Sunday have the fewest commits.

Conclusion

In this article, you learned how the Java client library makes it easy to stream JSON data into BigQuery. You can view the complete source code on GitHub. For more information about the Write API, including how to use committed and pending modes, see the BigQuery Storage Write API documentation.

By: Veronica Wasson (Technical Writer)
Source: Google Cloud Blog



For enquiries, product placements, sponsorships, and collaborations, connect with us at hello@globalcloudplatforms.com. We'd love to hear from you!


Our humans need coffee too! Your support is highly appreciated, thank you!

Total
0
Shares
Previous Article

Leveraging OpenTelemetry To Democratize Cloud Spanner Observability

Next Article

Evolving To A Programmable Cloud

Related Posts

BCI in Collaboration with IBM Advances Blockchain based Financial Services with Electronic Letter of Guarantee for Clients in Thailand

Bangkok, Thailand – 30 September 2020: IBM (NYSE: IBM) and the Blockchain Community Initiative (BCI) jointly announced that its electronic letter of guarantee (eLG) platform and network has successfully advanced and is now extending the scope and reach of its services and network to businesses of all sizes. The advanced capabilities on the BCI blockchain platform include equipping its network with true hybrid cloud and multi-vendor capabilities, disaster recovery and multi-region high availability. Commercial banks in Thailand manually issue guarantee letters worth billions of dollars to businesses annually as part of their guarantee process in case a buyer defaults. The negotiation between buyer, seller and bank can take up to nine days and the risk of forgery of paper documents could leave the seller without recourse.  Through Thailand Blockchain Community Initiative, now known as BCI (Thailand) Co., Ltd., 22 banks and 15 companies have joined the blockchain-based platform to support payment obligations, for enterprise auctions and other domestic trading procedures. The platform went live in late 2019 and currently handles an approximate value of US$ 300,000,000 in guarantee letters. It has proven to reduce letter of guarantee issuance time to less than a day. Some member companies report seeing a 200% reduction in transaction costs. eLG is designed to reduce the potential for errors and risk of fraud for all parties involved. “The blockchain platform is significant to BCI as it is aligned to our roadmap of using a hybrid deployment model to grow to overseas markets in the future. The Letter of Guarantee is just the first service offering from BCI. More products will be added in the near future. The platform also sets a strong foundation for BCI to scale and expand our services offerings on top of bringing speed and efficiency to more companies,” said Silawat Santivisat, Chairman of The Board, BCI (Thailand) Co., Ltd. “Blockchain is the key enabler for multi-industry connectivity platforms.  Blockchain allows businesses to rethink processes and inefficiencies, and with the right partners and technology platforms providing scalability, security and multi-cloud capabilities, these networks can grow efficiently across environments and regions.” “The major upgrade from Hyperledger Fabric v1.1 to Hyperledger Fabric v1.4 LTS posed huge business and technical challenges, with months of preparation and tremendous effort from all 15 participating entities working closely together on improving the business processes, security, governance, compliance and technical resources. The IBM team’s strong commitment and outstanding support to BCI made this upgrade to global best practices a great success with zero downtime,” said Siriwat Keatcharoensin, President of BCI (Thailand) Co., Ltd. “With IBM Blockchain as the technology foundation, and with the innovation milestone we achieved today, we are ready to welcome new members to our eLG platform and collectively bring maximum efficiency to business transactions in Thailand.” “IBM is proud to support BCI and its growing consortium in bringing the use of blockchain technology on the highly-secured IBM Cloud environment to address one of Thailand’s key pain points in executing financial transactions,” said  Patama Chantaruck as Vice President for Indochina Expansion and Managing Director of IBM Thailand. “First developed by members of the Thai banking community with IBM Garage for Cloud in 2016, the platform has incorporated best practices in bringing both bank and non-bank ecosystem players together quickly and effectively.” Large, medium or small companies in Thailand can easily start using BCI’s eLG services. Clients have the option to access the BCI portal through different models, including private on-premise access, via the cloud, or via a cloud broker. For more details, please go to https://en.bci.network/products. Media contactParanee Reymondon IBM Thailand Co., Ltd.Tel: +66 2 273 4164Email: paranee@th.ibm.com