9 ms·
Think of Schema changes much like a two-phase commit in a database (Which has more than two phases - I hate that term.) First, propose the new schema. You shou
by GauntletWizard 8y ago
Think of Schema changes much like a two-phase commit in a database (Which has more than two phases - I hate that term.)
First, propose the new schema. You should check in a version of your schema that contains both the fields from your new and old code. You can't do certain changes in this phase - If you're deleting fields, you can't do that yet. If you're changing column types, you should be careful; Hopefully your DAO already abstracts this, but you may need to roll that code change out first.
Then, make your application use the new schema. Start writing to the new columns as well as the old. Prepare a migration that will copy any data from old columns that needs to be. Your application should use the old data as the source-of-truth during this time, but when writing, it needs to write both versions of those fields. At this point, you can still easily roll back, and may want to, if you decide to proceed a different way for development.
Next, flip the source of truth. Your new fields should be the primary ones, but the old ones still need to be valid. You should be prepared to roll back still - The previous release should still be usable.
Once you're certain you're not going to roll back, stop reading/writing the old data. This is your hard flip, but once it's started, you can proceed leisurely. It doesn't matter - You've entered cleanup phase. Once that software change is finished, you can do a second schema change to delete old fields. (This is where the significant value of Schemaless is - You don't need to do anything in this second change, just let old data naturally clear itself out. Schemaless actually requires more attention to schema than a Schema-driven database, for this reason, but the iteration time can be quite fast if you have a good schema-control system, like, say, storing protobuf definitions in your version control and following their update rules[1])
All in all, this requires at least two periods of schema change and one period that involves multiple software rollouts. It's not simple, but nor is it that complex - And if done right, it's easy to roll other software changes at the same time, never losing velocity (aside from that which is spent reaching this target)
All of this requires that your application is well formed. Your application needs to be properly modularized, use reasonable DAOs or otherwise have complete understanding of how the data is accessed. It may require coordination between two projects that move, while not in lockstep, with gates on the other.
[1]https://developers.google.com/protocol-buffers/docs/proto#updating https://developers.google.com/protocol-buffers/docs/proto#up...
- desc 8y agoIndeed. It's doable, but requires a reasonably fast rollout tempo and a manageable level of technical debt. It is utterly impractical when you're deploying to individual customer sites, for customers who agree to an upgrade on timescales ranging from three months to five years... [edit] Part 1, the automated schema updates, I'm right alongside. I can't fathom how people can operate with manual schema updates all the time. But part 2 is IMO wishful thinking outside of a deployed environment your own team can upgrade pretty much at will.
- GauntletWizard 8y agoAbsolutely. It only works for Managed or SAAS solutions. You need to be prepared to deal with much wider ranges of schema problems on embedded devices - Which is a good argument for Schemaless and a good version control document, though you can absolutely take it too far in terms of compatibility.
- joatmon-snoo 8y agoAll good points. It's worth mentioning that this is exactly why protobuf doesn't by default fail when it gets a field it doesn't recognize (there might be a flag to control strict validation, but there is a reason that "required" fields were taken out in proto3). From the source code: https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/unknown_field_set.h https://github.com/protocolbuffers/protobuf/blob/master/src/... // An UnknownFieldSet contains fields that were encountered while parsing a // message but were not defined by its type. Keeping track of these can be // useful, especially in that they may be written if the message is serialized // again without being cleared in between. This means that software which // simply receives messages and forwards them to other servers does not need // to be updated every time a new field is added to the message definition. A bit more detail: For those who don't work with protos, they're the thing that Google created to use instead of XML before JSON was a thing. To use them, you define a message schema consisting of fields, where every field has (1) a number that will uniquely identify that field in the message, (2) a type for the field, and (3) a name for the field. The protoc compiler then generates code that decodes and encodes that specific message type for you. Now think about what happens when a new field is added to that message type and the code is re-generated. That change is probably going to be in at least three different binaries (and it's often more): the client, the frontend, and the backend. There's no way to guarantee that every single client, frontend, and backend picks up that change at the same time - it has to go out incrementally. So if client v2 talks to frontend v1 and gets routed to backend v2, frontend v1 damn well better not crash if it sees a new field.