Ask HN: How do you version your data?
Superficially, It ought to be possible to use SemVer major.minor.patch (patch can change data and alter the schema but not extend the schema, minor can extend the schema in backward compatible ways like adding a column, and major version numbers are used for backward-incompatible changes), but in practice I don't see this being applied consistently, especially if data dumps are only occasional.
Is your data versioned? How?
57 comments
[ 200 ms ] story [ 2606 ms ] threadI've seen three schemes used in production:
- single number versioning (V1->V2->V3->V4) - this is the scheme Android's sqlite helper insists on https://developer.android.com/reference/android/database/sql...
- full version numbers, but well defined upgrade tracks enforced in code.
- token based (e.g. a list of upgrades that have been applied)
That is, imagine that after a version of the data is released it increases 10X, but the schema doesn't change. That might still be worth making a release for, right?
Typically what I see in the wild are data dumps on some sort of semi-regular schedule such as weekly, monthly, or annually, "versioned" with the timestamp or some derivative thereof (eg. "August 2016").
This is much better than nothing, but just as a source dump for each release of an application or other software in a tarball is not as useful as being able to check out a tagged release from a repo, so too versioned data releases that can be checked out with a tag from a repo would be more helpful that a data tarball.
What I have done in the past is to prefer lazy migrations (migrate once you access an old record), rather than migrating everything in batch.
Also a good idea to archive data that hasn't been used, moving it outside your primary database.
I'm not just interested in versioning schema changes, but data changes as well.
I have used Liquibase in production for handling data changes for small-scale data (in megabytes) for dev/test/mock data and/or values in preconfigured application-controlling tables, but I wouldn't use it for gigabytes of transactional data.
[0]: https://medium.com/@clord/for-migration-of-schemas-use-versi...
BTW, why using shell scripts to write migrations is so unpopular? We have lots of language/framework specific solutions or some «language agnostic» tools using plain SQL, both ways are rather limiting.
and if you never merge the forks, there is no problem. they likely won't share a production database anyway, right?
We do have a hash of all of the contents of the table, but the schema "version" is just the list of migrations that have been run. We run many different instances of our applications on several different "versions" and from time to time pull individual migrations back into earlier releases for hotfixes etc. The overall version is the instance, environment, and this hash.
Practically speaking, when we're handling the files ourselves though, it's instance, environment and backup date.
There are edge cases and it's not perfect (around ordering mostly), but it's incredibly rare for us to run into issues with this schema / data migration approach. We've found that even with a bunch of environments and a couple thousand migrations, data versioning isn't that serious a problem for us.
It's worked reasonably well so far.
[0] https://github.com/pachyderm/pachyderm
https://en.m.wikipedia.org/wiki/Slowly_changing_dimension
Option 2 is preferred for very long time restore say after 15 years as there may not be a way to restore to Oracle/MS Products which may not have run time ecosystem after 15 years.
You could add a separate "changes" feed to help API consumers, or inline version info to the main API results.
Check out PostgreSQL's LISTEN and NOTIFY:
https://www.postgresql.org/docs/9.6/static/sql-listen.html
https://www.postgresql.org/docs/9.6/static/sql-notify.html
A related HN discussion a few years ago: https://news.ycombinator.com/item?id=6689213
If not, ask the client to provide an HTTP callback endpoint, and ping it to notify of new fresh data on your side. Using WebSocket is also an option.
Granted, I was interested in the data within the table not the structure and the size was manageable.
In terms of storing the data, we had a system where the content would be zipped after the content developers were done with the authoring and sent to a place which would convert it into appropriate JSON documents with the metadata and versioning information stored in the DB while the the document content could be stored in the cloud or a document database like Mongo or just Postgres. The content authors only knew excel who were trained to follow a schema while writing the content. That was like a low cost CMS. You can update only the content that has a diff or the entire content depending on how well you can identify a diff for the content. The entire content makes it simple.
Data in motion - messages - always look something like this:
and the parsers know to reject messages with versions greater than what they can parse; depending on the system, they can also be backwards compatible. Time-sent turns out to be a lifesaver in debugging. You might also need TZ of time-sent, depends on the domain.Versioning data at rest tends to be a little squirrely depending on the domain. Do you migrate data or do you not? what's your uptime? streaming or batch? Sometimes I version the actual table names, sometimes I migrate.. it depends. My preference is for migration to keep a consistent system, but that is not always feasible.
I'm a huge fan of SQL - it defines the data shape and structures the transforms possible on it, along with allowing a strong separation of data and computation. Postgres is my friend; I heavily use foreign keys and constraints on the schema. That way the data is reliable. (if your data isn't reliable, your schema should reflect that too of course). If I need to have multiple versions of data running at the same time, multiple tables or migrating is cleaner than versioning the specific rows. Otherwise you wind up with nulls and driving schema logic out into your code.
Typically I tack a unix time of insert into the rows for later analysis. You might also care to insert the current application name+version into the rows to catch any bugaboos when that changes.
After being inspired by Hans Werner's answer here (several terabytes of binary data, 50000+ revisions), I chose Subversion. It's not conventional, but works very well in practice.
https://stackoverflow.com/questions/127692/svn-performance-a...
You get:
a) Natural audit trail & notes on data modification
b) Managed central dump of data => multiple, distributed local copies that you don't need to worry about keeping in sync. Just delete the cache and the data access API (see below) will check it out automatically again when you request the file.
Data access is encapsulated via an API that manages a /home/datadump/ of cached, revisioned files. You refer to the file using it's name + revision number (see below). I guess tags and branches can be used for more natural revison numbers, but I need to investigate whether Subversion's cheap copy works well in practice for this. They might be the perfect solution for you major.minor.patch needs?
User Code --> get_data("<file_path/file_name>", "r=38") --> API checks the cache for file_path/file_name_r=38. If it's not there it checks it out using a read-only user id and puts it in the cache and returns the path to /home/datadump/file_path/file_name_r=38.
Unusual, but works just fine for our purposes of mostly-read-only large files that need a revision history.
An idea that I did not explore was using ZFS or other revisioned file systems. Another "crazy" idea that works just fine for some folks is using Binary blobs in a database; not sure about size limits, though.
http://snowplowanalytics.com/blog/2014/05/13/introducing-sch...
SemVer doesn't work for data - for one thing, there is no concept of a "bug" in your data (so patches are meaningless).
We have hundreds of companies actively using SchemaVer via the Snowplow (https://github.com/snowplow/snowplow/) and Iglu (https://github.com/snowplow/iglu/) projects.
Is there not? Lets say that you're changing the data in your database/data structure from state X to state Y. This involves transforming the data in some tables/data structures from the old structure to the new.
Lets say that you do this and it's all fine, the upgrade goes great. But then you discover there's a problem with the data upgrade.
While you have transformed the data into the new format, it's not been done right. So you actually need a second data change to ensure that your data upgrade is semantically equivalent to the data that went before it, even though the data conforms to your new schema.
Would that not count as a bug in your data?
Take for example a GetUserStatistics() call which provides a list of userids and the users last login date.
A client might be using this list to get statistics on system usage.
If you change the codebase to add the concept of a test user and add an isTestUser column to GetUserStatistics() you have broken the contract with your users.
You had an implicit contract based on shared understanding of the data.
Now of course to correctly determine user usage statistics you need to exclude the test users by checking the new column.
As a consumer it is what I care about.
When transforming the schema, you frequently have associated changes that you apply to transform the data from one form (in the 'before' schema) to another (in the 'after' schema). These transformations are code that can have bugs like any other.
In cases like these you can have data that is in the right format, but isn't correct, and can need a second change (to the data only) to correct it.
I think you have a certain amount of fuzziness around the idea of an "interaction" with the data. It would probably help to think about compatibility and breaking changes in terms of reads vs. writes in order to get the determinism you're looking for and better alignment with SemVer.
That is, if a client using the previous schema can still do reads and writes without the data being invalid, you have forward compatibility, and this qualifies as an PATCH.
If a client using the previous schema can still do reads against the new schema without the data being invalid but not writes, that would qualify as a MINOR change.
(Aside: write-but-not-read compatible changes are possible, but are uncommon in practice)
A change that can prevent a client using the old schema from doing valid reads against the new schema (eg. a column is renamed or removed) would be a MAJOR change.
Thoughts?
https://www.w3.org/2001/tag/doc/versioning#iddiv371153984
which has a very succinct explanation of forwards and backwards compatibility as it relates to producers and consumers.
It's high time we did a second draft of SchemaVer which explains it in terms of forwards/backwards compatibility; the actual behavior of it (when to bump etc) would barely change.
Breaking writes would not be considered a minor change in semver.
SQL Server has Master Data Services. There's also Talend. There are other master data management tools available.
Depending on how you use your data, there are tools for improving data quality that tie in with those as well.
I was thinking more along the lines of a versioned data "release".