{"id":18753032,"url":"https://github.com/spotify/simple-bigtable","last_synced_at":"2026-03-01T15:33:53.547Z","repository":{"id":54553218,"uuid":"52626276","full_name":"spotify/simple-bigtable","owner":"spotify","description":null,"archived":false,"fork":false,"pushed_at":"2023-03-22T19:46:35.000Z","size":175,"stargazers_count":35,"open_issues_count":8,"forks_count":16,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-12-26T16:51:09.870Z","etag":null,"topics":["bigtable","client","java","wrapper"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spotify.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-02-26T19:05:11.000Z","updated_at":"2024-12-22T00:15:09.000Z","dependencies_parsed_at":"2025-04-13T00:31:39.562Z","dependency_job_id":"cfe76c67-fcc4-4fb8-a0da-e63f072e357f","html_url":"https://github.com/spotify/simple-bigtable","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/spotify/simple-bigtable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Fsimple-bigtable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Fsimple-bigtable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Fsimple-bigtable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Fsimple-bigtable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spotify","download_url":"https://codeload.github.com/spotify/simple-bigtable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Fsimple-bigtable/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29973320,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T15:29:09.406Z","status":"ssl_error","status_checked_at":"2026-03-01T15:28:28.558Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bigtable","client","java","wrapper"],"created_at":"2024-11-07T17:23:54.221Z","updated_at":"2026-03-01T15:33:48.538Z","avatar_url":"https://github.com/spotify.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simple Bigtable\n\n## Overview\n\n[Cloud Bigtable](https://cloud.google.com/bigtable/docs/) is a datastore supported by Google for storing huge amounts\nof data and maintaining very low read latency. The main drawback to using Bigtable is that Google does\nnot currently have an official asynchronous client. Within Spotify we have been using the RPC client which is\na pain to use. This library aims to fix that by making the most common interactions with Bigtable clean and easy\nto use while not preventing you from doing anything you could do with the RPC client.\n\nTo import with maven, add this to your pom:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.spotify\u003c/groupId\u003e\n    \u003cartifactId\u003esimple-bigtable\u003c/artifactId\u003e\n    \u003cversion\u003eLATEST_RELEASE\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Raw RPC Client vs Bigtable Client Comparison\n\n### Using The RPC Client\n\nTo give an example of using the base RPC client (which gives the `BigtableSession` object), this is how you would\nrequest a single cell from Bigtable.\n```java\nString projectId;\nString zone;\nString cluster;\nBigtableSession session;\n\nString fullTableName = String.format(\"projects/%s/zones/%s/clusters/%s/tables/%s\",\n        projectId,\n        zone,\n        cluster,\n        \"table\");\n\n// Could also use a filter chain, but you can't actually set all the filters within the same RowFilter object\n// without a merge or chain of some sort\nfinal RowFilter.Builder filter = RowFilter.newBuilder().setFamilyNameRegexFilter(\"column-family\");\nfilter.mergeFrom(RowFilter.newBuilder().setColumnQualifierRegexFilter(ByteString.copyFromUtf8(\"column-1\")).build());\nfilter.mergeFrom(RowFilter.newBuilder().setCellsPerColumnLimitFilter(1).build()); // By default it is 1\n\nfinal ReadRowsRequest readRowsRequest = ReadRowsRequest.newBuilder()\n        .setTableName(fullTableName)\n        .setRowKey(ByteString.copyFromUtf8(\"row\"))\n        .setNumRowsLimit(1)\n        .setFilter(filter.build())\n        .build();\n\nfinal ListenableFuture\u003cList\u003cRow\u003e\u003e future = session.getDataClient().readRowsAsync(readRowsRequest);\nfinal ListenableFuture\u003cCell\u003e cell = FuturesExtra.syncTransform(future, rows -\u003e {\n  // This doesnt actually check if the row, column family, and qualifier exist\n  // IndexOutOfBoundsException might be thrown\n  return rows.get(0).getFamilies(0).getColumns(0).getCells(0);\n});\n```\n\n### Bigtable Client\n\nThe goal of this client is to let you query what you want with minimal overhead (there should\nbe no need to create all these filter objects) as well as give you the object you want without\nneeding to constantly convert a list of rows down to a single cell.  Note that these examples\nuse a String as a row key.  Bigtable keys are really byte arrays. Strings in this api is just\na convenience.  Under the cover the string \"row\" is converted to a ByteString.  In reality\nyou should use byte arrays as keys as that will\nbe more efficient.\n \nHere is the same query as above using this client wrapper.\n```java\nString projectId;\nString zone;\nString cluster;\nBigtableSession session;\n\nBigtable bigtable = new Bigtable(session, projectId, zone, cluster);\nfinal ListenableFuture\u003cOptional\u003cCell\u003e\u003e cell = bigtable.read(\"table\")\n    .row(\"row\")\n    .column(\"family:qualifier\") // specify both column family and column qualifier separated by colon\n    .latestCell()\n    .executeAsync();\n```\n\n## Performing Reads\n\nThe goal of this client is to make the most tedious and common interactions with Bigtable as painless as possible.\n Therefore reading data is an extremely large focus. Here are some examples of reading data.\n\nGet full column family within row\n```java\nfinal ListenableFuture\u003cOptional\u003cFamily\u003e\u003e family = bigtable.read(\"table\")\n    .row(\"row\")\n    .family(\"family\")\n    .executeAsync();\n```\n\nGet multiple columns within a row (Currently all need to be in the same column family but hopefully that gets fixed)\n```java\n// Get the entire column\nfinal ListenableFuture\u003cList\u003cColumn\u003e\u003e family = bigtable.read(\"table\")\n    .row(\"row\")\n    .family(\"family\")\n    .columnQualifiers(Lists.newArrayList(\"qualifier-1\", \"qualifier-2\"))\n    .executeAsync();\n\n// Get the latest cell in each column\nfinal ListenableFuture\u003cList\u003cColumn\u003e\u003e family = bigtable.read(\"table\")\n    .row(\"row\")\n    .family(\"family\")\n    .columnQualifiers(Lists.newArrayList(\"qualifier1\", \"qualifier2\"))\n    .latestCell()\n    .executeAsync();\n```\n\nGet columns within a single family and within column qualifier range\n```java\nfinal ListenableFuture\u003cList\u003cColumn\u003e\u003e columns = bigtable.read(\"table\")\n    .row(\"row\")\n    .family(\"family\")\n    .columns()\n    .startQualifierInclusive(startBytestring)\n    .endQualifierExclusive(endBytestring)\n    .executeAsync();\n```\n\nGet cells between certain timestamps within a column\n```java\nfinal ListenableFuture\u003cList\u003cCell\u003e\u003e cells = bigtable.read(\"table\")\n    .row(\"row\")\n    .column(\"family:qualifier\")\n    .cells()\n    .startTimestampMicros(someTimestamp)\n    .endTimestampMicros(someLatertimestamp)\n    .executeAsync();\n```\n\nGet the latest cell of a certain value within a column\n```java\nfinal ListenableFuture\u003cOptional\u003cCell\u003e\u003e cells = bigtable.read(\"table\")\n    .row(\"row\")\n    .column(\"family:qualifier\")\n    .cells()\n    .startValueInclusive(myValueByteString)\n    .endValueInclusive(myValueByteString)\n    .latest()\n    .executeAsync();\n```\n\nGet the latest cell of a between 2 timestamps within a column for multiple rows\n```java\nfinal ListenableFuture\u003cList\u003cRow\u003e\u003e cells = bigtable.read(\"table\")\n    .rows(ImmutableSet.of(\"row1\", \"row2\"))\n    .column(\"family:qualifier\")\n    .cells()\n    .startTimestampMicros(someTimestamp)\n    .endTimestampMicros(someLatertimestamp)\n    .latest()\n    .executeAsync();\n```\n\nGet the multiple column families and column qualifiers (will match all combinations)\n```java\nfinal ListenableFuture\u003cList\u003cRow\u003e\u003e cells = bigtable.read(\"table\")\n    .row(\"row\")\n    .families(ImmutableSet.of(\"family1, family2\"))\n    .columnQualifiers(ImmutableSet.of(\"qualifier1\", \"qualifier2\")\n    .cells()\n    .startTimestampMicros(someTimestamp)\n    .endTimestampMicros(someLatertimestamp)\n    .latest()\n    .executeAsync();\n```\n\nGet all rows between different ranges or with certain specific keys\n(these functions add rows to the row set, instead of filtering)\n```java\nfinal ListenableFuture\u003cList\u003cRow\u003e\u003e rows = bigtable.read(\"table\")\n    .rows()\n    .addRowRangeOpen(myStartKeyOpen, myEndKeyOpen) // add an exclusive range\n    .addRowRangeClosed(myStartKeyClosed, myEndKeyClosed) // add an inclusive range\n    .addKeys(extraKeys) // add some keys you always want\n    .executeAsync();\n```\nNote that currently there is no half open, half closed range.\n\n## Other Operations\n\nThe client supports other Bigtable operations as well, with hopefully the rest of all possible operations coming\nsoon.\n\n### Mutations (Writes, Deletions)\n\nMutations are performed on the row level with many mutations possible within a single call. Mutations include\nwriting new values as well as deleting a column, column family, or an entire row and all data help in each.\n\nWrite a new cell within a column\n```java\nfinal ListenableFuture\u003cEmpty\u003e mutation = bigtable.mutateRow(\"table\", \"row\")\n    .write(\"family:qualifier\", ByteString.copyFromUtf8(\"value\"))\n    .executeAync()\n```\n\nPerform multiple writes in different columns setting an explicit timestamp on some\n```java\nfinal ListenableFuture\u003cEmpty\u003e mutation = bigtable.mutateRow(\"table\", \"row\")\n    .write(\"family:qualifier\", ByteString.copyFromUtf8(\"value-1\"), timestampMicros)\n    .write(\"family\", \"qualifier\", ByteString.copyFromUtf8(\"value-2\"))\n    .executeAync()\n```\n\nDelete a column and then write to the same column\n```java\nfinal Empty mutation = bigtable.mutateRow(\"table\", \"row\")\n    .deleteColumn(\"family:qualifier\")\n    .write(\"family:qualifier\", ByteString.copyFromUtf8(\"brand-new-value\"))\n    .execute()\n```\n\n### ReadModifyWrite (Atomically Update or Append To A Column)\n\nReadModifyWrite is useful for either incrementing the latest cell within a column by a long or appending bytes\nto the value. If the column is empty, is will write a new value. Once again this operation is on the row level\nwith multiple ReadModifyWrites possible in a single request.\n\nIncrement a couple counter columns and append a value to another\n```java\nbigtable.readModifyWrite(\"table\", \"row\")\n    .read(\"request-numbers:number-1\")\n    .thenIncrementAmount(1L)\n    .read(\"request-numbers:number-2\")\n    .thenIncrementAmount(5L)\n    .read(\"family:values\")\n    .thenAppendValue(ByteString.copyFromUtf8(\"new-value\"))\n    .executeAsync();\n```\n\n### SampleRowKeys\n\nSample some row keys from a table.\n```java\nfinal List\u003cSampleRowKeysResponse\u003e sampleRowKeys = bigtable.sampleRowKeys(\"table\").execute();\n```\n\n### CheckAndMutateRow - NOT YET IMPLEMENTED\n\nPerform a read and a set of mutations depending on whether the read returns data. This is not yet implemented but\nhere are some ideas on how this operation might be implemented in the future.\n\nHave the check specified like a read, then allow mutations to be added.\n```java\nbigtable.checkAndMutateRow(\"table\", \"row\")\n    .column(\"family:qualifier\")\n    .cells()\n    .endTimestampMicros(timestamp)\n    .ifExists()\n    .deleteColumn(\"family:qualifier\")\n    .write(\"family:qualifier\", \"had-data\")\n    .ifDoesNotExist()\n    .write(\"family:qualifier\", \"did-not-have-data\")\n    .executeAsync();\n```\n\nPass in Bigtable protobuf objects, kind of against the purpose of the library but keeps things simple.\n```java\nbigtable.checkAndMutateRow(\"table\", \"row\")\n    .rowFilter(someRowFilter)\n    .ifExists(someMutation)\n    .ifExists(someOtherMutation)\n    .ifDoesNotExist(someOtherMutation)\n    .executeAsync();\n```\nPull requests with other ideas are encouraged.\n\n### Table and Cluster Admin Operations - NOT YET IMPLEMENTED\n\nIt is unclear whether there is a need this wrapper to provide the admin operations, though it would be pretty easy\nto include. \n\n## How to Release\n- A local build and deploy is the easiest way to make a release at this point.  For setup follow instructions in:\n   [scio instructions](https://github.com/spotify/scio/wiki/How-to-Release#prerequisites) modified to work with maven.\n   For credentials to push to [sonatype](https://oss.sonatype.org/content/repositories/snapshots/com/spotify/simple-bigtable/), create an access token with your sonatype login and place the access token\n   in your maven settings.xml as in:\n\n```xml\n    \u003cserver\u003e\n      \u003c!-- sonatype repository --\u003e\n      \u003cid\u003eossrh\u003c/id\u003e\n      \u003cusername\u003eaccess-token-name\u003c/username\u003e \u003c!-- access token tied to an account sonatype.org --\u003e\n      \u003cpassword\u003eaccess-token-password\u003c/password\u003e\n    \u003c/server\u003e\n```\n\n-   `mvn clean javadoc:jar source:jar deploy` should do the rest.\n\n-   Release deployments (from mvn deploy) will be put into a staging area at sonatype.  Read the following to release the staged deployment [how to release](http://central.sonatype.org/pages/releasing-the-deployment.html) and [maven](http://central.sonatype.org/pages/apache-maven.html).\n\n-    With effort we could get automatic deployments via travis.  The travis build console is here:\n     [travis.org](https://travis-ci.org/spotify/simple-bigtable).  You'll need access to travis.org (not .com) to access\n     the builds.\n   \n## Open Problems and Questions\n\n- One problem is that currently it is not really possible to do queries for nested lists. For example there really is\n no way to do a ColumnRange within a RowRange or request multiple columns within different column families. \n Something like `BigtableColumnsWithinFamilies` could be added where it keeps track of needing different column\n families but that is confusing. Another option is adding the filtering methods to every Read object which would also\n be super confusing.\n\n## Code of conduct\nThis project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.\n\n[code-of-conduct]: https://github.com/spotify/code-of-conduct/blob/master/code-of-conduct.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspotify%2Fsimple-bigtable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspotify%2Fsimple-bigtable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspotify%2Fsimple-bigtable/lists"}