0

we have several technologies accessing the same database. At the moment, Ruby/Rails is used to create migrations when making changes to the database. The question is a simple one:

Is it possible for our DBAs to make changes to the database (not using Ruby migrations) without stepping on the Ruby devs toes and breaking the Ruby web application?

If so, some generic details about how to get started or pointed in the right direction would be great! Thanks.

bbqchickenrobot
  • 3,592
  • 3
  • 45
  • 67
  • 2
    While this question is more for superusers forum, it is still programming-related. I imagine it's a common occurrence with wide organizational development teams and it probably should get some attention here. So I'll vote to keep it open if it closes. – vol7ron Apr 29 '16 at 00:15
  • 1
    Yep, I've actually had to dig out of a hole caused by just this sort of thing, definitely happens ... more than it should IMO. – GoGoCarl Apr 29 '16 at 00:43

3 Answers3

4

I can tell you from experience that this is not the best idea, one that you will eventually regret and later, inevitably, reverse. But I know that it does come up. I've had to do them (against my will or in case of extreme emergencies).

Given the option, I'd push back on it if you can in favor of any solution that bring the SQL closer to the repository and further away from a "quick fix" to the database directly. Why?

1) Your local/testing/staging/production databases will diverge, eventually rendering your code untestable in a reliable way

2) You won't be able to regenerate your database from "scratch" to match production

3) If the database is ever clobbered, you won't be able to re-create it in any sensible way.

DBA's generally don't care about these things until something in the code breaks, and they want you to figure it out. But, for obvious reasons, that now becomes quite difficult.

One approach I have taken that seems to make everyone happy is to do the following:

1) Commit to having ALL database changes, big or small, put into a repository with the code. This means that everything that has happened to the database is all together in one place.

2) Each change, or set of changes, should be a migration. A migration can be simply running an SQL file. But, it should be run from within a migration for all the testability benefits.

So, for example, let's say you have a folder structure like:


- database_updates
-- v1
--- change_1.sql
--- change_2.sql
-- v2
--- change_3.sql
--- change_2_fix.sql

Now, let say you want to make a change or set of change via SQL. First, create a new version folder, let's call it "v1". Next, put your SQL scripts in this folder. Finally, create a migration:

def change
  # Read all files in v1 folder, and run the SQL
end

(I have code that does this, happy to share the gist if you find yourself using this approach)

Since each migration is transactional, any of the scripts that fail will cause all of them to fail.

Now, let's say you have the next set, v2. Same exact thing. And, we have a history of these "versioned" changes, so we can look at the migration history and see what's been run, etc.

As a power user note, this set up also allows for recourse if things fail; in these cases, we can opt to go back to v1:

def up
  # run v2 scripts
end

def down
  # run v1 scripts
end

For this to work, v1 and v2 would need to be autonomous -- that is, they can destroy and rebuild entities without any dependencies. If that's not what you want, just stick with the change method.

This would also allow you to test for breaking changes. Let's say it is reported that something doesn't work anymore with v6. You can rollback your database migrations to v5, v4, etc (because you are doing a migration per folder) and test to see when the test broke, and correct it with v7.

Anyway, the end game of it all is that you can safely check out this project from a repository, create your database, run rake db:migrate and know that your database structure resembles exactly what is deployed elsewhere. And, worst case, if your database gets clobbered, you can just run all your scripts from v1 - vN and end up with your database back again.

For DBA's everything remains SQL for them, they can just send you a file or set of files for you to run.

If you want to get fancy, you could even write a migration generator that knows how to handle a line like rails g migration UpdateDBVersion version:v7 to take care of the repetitive boilerplate.

GoGoCarl
  • 2,519
  • 13
  • 16
1

As long as everyone relies on the same updated schema.rb or structure.sql, everyone will share the same database 'version'.

See this SO answer for more insight.

Community
  • 1
  • 1
Abraham Sangha
  • 336
  • 1
  • 9
  • 1
    I would argue that because the database migrations and schema are part of the checked in code, DB changes should be in the realm of engineering. If the DBA's want changes, they should be included in the standard way, with migrations, and tested with the application prior to release. That's the only way to ensure DBA's aren't introducing breaking changes. – Mitch Apr 29 '16 at 00:05
  • 2
    @Mitch that's assuming that all the teams that use and work on the database (DBAs and contributors) are all using Rails. Sometimes this doesn't happen and sometimes it is quicker to apply a hotfix directly to a database, either using a GUI or good old-fashioned sql command line interface. – vol7ron Apr 29 '16 at 00:16
1

Changes to the database, tables, or indexes should be made using ActiveRecord migrations whenever possible. This specifically ensures that development and test environments remain logically in sync. Remember that developers must be capable of accurate development and testing against the same database structure as occurs in the production environment, and QA teams must be able to adequately test such changes.

However, some database features are not actually supported by ActiveRecord migrations, and may only be applied directly to the database. These features are often database-specific, such as any of the following:

  • Views
  • Triggers
  • Stored procedures
  • Indexes with function-based columns
  • Virtual columns

Essentially any database-specific features that don't have an ActiveRecord abstraction will be made directly to the database.

Sometimes, however, other applications require the addition of tables, columns, or indexes in order to operate properly or efficiently. These other applications may simply be used to view/report against the database, or they may be substantial business applications that have their own independent database requirements and separate development teams. Occasionally, a DBA may have to step in and create an index or provide some optimization needed to solve a real-world production performance issue.

There are simply far too many situations for shared database management to give a definitive answer. Depending on the size of the organization and the complexity of the needs for the shared management, there may be many ways to solve the problem of a shared database schema that are specific to the application or organization.

For instance, I have worked on applications that shared a database with as many as 10 other applications, each of which "owned" portions of the schema and shared other portions with the other teams, all mediated through the DBA group. In situations such as this, the organizational structure and change control process may be the only means of solving this problem.

Whichever the situation, some real-world suggestions may help avoid problems and mitigate maintenance woes:

  • Offer to translate SQL DDL commands into ActiveRecord migrations, where possible, so that DBAs can accomplish their needs, and the application team can still appropriately maintain the schema
  • Any changes made outside the ActiveRecord migration should be thoroughly tested for impact to the project in a non-production environment by the same QA resources that test the actual Rails application
  • Encapsulate any external changes in a .sql file and include the file as part of the project in version control
  • If the development team is using the same database product in development (some cannot, due to licensing or complexity), those changes should be applied to the developer database instances, as well
  • It's best if you can apply the changes during a migration, even just by calling the relevant CLI tools as a migration step - the exact mechanism will be database-dependent, as well
  • Try to avoid doing this more than is absolutely necessary, as this can significantly reduce the database independence of the application, even between versions of the same database product (limiting upgrade opportunities)
Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43