Theo tôi quan sát, database versioning (DB versioning – phiên bản hoá cơ sở dữ liệu – CSDL) thường là vấn đề khó khăn nhất đối với các nhóm triển khai Agile trong việc thực hành CI và CD; thậm chí nhiều nhóm phát triển không nhận ra vấn đề này.

Mọi lập trình viên ngày nay đều hiểu sự quan trọng của những công cụ quản lý source code và có thể sử dụng thành thạo SVN, Git…, tuy nhiên ít người nhận biết được tầm quan trọng và biết cách quản lý những thay đổi trên CSDL. Tại sao lại như vậy? Bởi theo phương pháp phát triển phần mềm truyền thống, CSDL thường được coi như một phần đặc biệt quan trọng và được thiết kế rất chi tiết trong giai đoạn thiết kế phần mềm; và gần như không được phép thay đổi trong quá trình phát triển. Việc thiết kế CSDL cũng thường được thực hiện bởi những người đặc biệt quan trọng nhằm đảm bảo thiết kế tối ưu và đáp ứng tầm nhìn lâu dài của ứng dụng.

Tuy nhiên, phương pháp phát triển phần mềm mới đã thay đổi hoàn toàn cách suy nghĩ và sử dụng CSDL; phương pháp phát triển hướng CSDL (DB oriented programming) đã không còn được sử dụng. Có nghĩa là, CSDL giờ đây chỉ là một thành phần trong phần mềm đơn thuần, không phải là một phần “bất khả xâm phạm”, càng không phải là thành phần đầu tiên được nghĩ tới khi thiết kế kế hệ thống. Dữ liệu vẫn luôn đặc biệt quan trọng, nhưng cấu trúc của dữ liệu (thiết kế CSDL) thì không hẳn. Trong phương pháp phát triển phần mềm Agile, mọi thành viên trong nhóm phát triển đều có thể thay đổi thiết kế DB bất cứ lúc nào nhằm phục vụ cho chức năng mình đang phát triển. Điều này dẫn đến tần suất thay đổi thiết kế CSDL lớn hơn; và vấn đề quản lý thiết kế CSDL cũng phát sinh.

Thứ nhất, khi một lập trình viên thay đổi CSDL, những thành viên khác trong nhóm phát triển không nhận biết được thay đổi này. Vấn đề này được thực hiện rất tốt với source code version control như SVN hay Git; những thành viên trong nhóm chỉ cần check-out là nhận được những thay đổi trên source code. Thứ hai, khi những thay đổi này gây ra xung đột (conflict), nhóm sẽ giải quyết như thế nào? Vấn đề này phức tạp hơn rất nhiều; vì mỗi thay đổi trên CSDL sau khi được áp dụng sẽ không thể roll-back; không đơn giản như cách chúng ta giải quyết xung đột trên source code.

Theo tôi quan sát, những nhóm phát triển thường áp dụng những phương pháp sau:

  • Sử dụng môi trường chia sẻ. Cách dễ nhất (và đôi khi là cách tốt nhất) là sử dụng môi trường phát triển chia sẻ (shared environment); mỗi lập trình viên làm việc trên máy tính của mình với source code được check-out và phát triển hoàn toàn độc lập, nhưng sử dụng chung một DB server. Phương pháp này có rất nhiều điểm lợi, đặc biệt là đảm bảo một môi trường chung với mọi sự thay đổi được cập nhật kịp thời tới mọi lập trình viên. Tuy nhiên, vẫn có ít nhất 2 vấn đề gặp phải:
    • Hiệu năng. Trong phần nhiều dự án (không xử lý dữ liệu lớn), hiệu năng từ việc sử dụng môi trường chia sẻ thấp hơn nhiều hiệu năng xử lý trên máy tính cá nhân (vấn đề đường truyền, phân chia module…); gây ảnh hưởng tới năng suất làm việc.
    • Quản lý revision. Đây là vấn đề không có giải pháp toàn diện. Chúng ta sẽ xử lý thế nào với trường hợp sau: Sau một vài thay đổi, nhóm phát hiện ra rằng một số thay đổi gần đây không đúng và muốn roll-back? Vấn đề này thường xuyên xảy ra giống như việc chúng ta phải revert những commit lỗi khi sử dụng source code version control. Nhưng chúng ta không thể làm vậy với CSDL.
  • Tạo những script tương ứng với từng thay đổi. Ý tưởng ở đây là, nhóm lưu trữ một CSDL ổn định và tương ứng với mỗi thay đổi, từng script sẽ được tạo ra. Trong trường hợp cần revert commit, nhóm sẽ restore CSDL này và áp dụng từng script tương theo tuần tự tới trước commit cần revert. Vấn đề khác có thể nảy sinh, bằng cách nào những lập trình viên biết cách tạo những script theo thứ tự hợp lý khi việc phát triển, commit của họ được thực hiện song song trong khi sự thay đổi trên CSDL cần thực hiện tuần tự? Bằng cách nào nhóm có thể phát hiện ra những xung đột trên CSDL?

Tuy vậy, ý tưởng về việc scripting DB (kịch bản hoá CSDL) là một ý tưởng hay, và được phát triển thành DB versioning. Cơ bản có thể được mô tả như sau:

  • Mọi thay đổi trên CSDL phải được tạo bằng script. Thay vì thực hiện tạo một bảng mới qua công cụ có giao diện người sử dụng, chúng ta viết thành câu lệnh CREATE TABLE và lưu trong file SQL.
  • CSDL cũng được quản lý bởi source code version control. Khi mọi thay đổi trên CSDL là script, chúng ta hiểu rằng CSDL cũng là source code và những file này được quản lý bởi source code version control là điều hợp lý.
  • Mọi script thay đổi trên CSDL gắn với từng phần phát triển phải được commit đồng thời. Ví dụ, lập trình viên thực hiện user story “quản lý khách hàng” cần check-in source code và script tạo ra bảng KhachHang trong một commit. Điều này giúp cho việc quản lý (đặc biệt là revert) trở nên đơn giản hơn.
  • Mọi thay đổi trên CSDL được kiểm tra trong quá trình build hoặc start-up của ứng dụng. Theo tôi, việc kiểm tra nên được thực hiện sớm nhất có thể (tốt nhất là trong quá trình build) nhằm giảm thiểu thời gian lập trình viên nhận biết những thay đổi trên CSDL. Nếu việc áp dụng những script này không thành công, quá trình build hoặc khởi chạy ứng dụng sẽ thất bại; qua đó lập trình viên nhận biết được sự thay đổi và những xung đột đang xảy ra trên CSDL.
  • Các version của sự thay đổi được lưu trữ tại chính CSDL đó. Một cách tiếp cận đơn giản là tạo ra một bảng lưu trữ lịch sử những script đã được áp dụng trên chính CSDL đó.

Kỹ thuật trên sẽ khiến CSDL và những thay đổi trên CSDL (script) được coi là source code; giúp lập trình viên dễ dàng quản lý hơn rất nhiều, cả về tư tưởng lẫn kỹ thuật.

Hiện nay, thị trường cung cấp rất nhiều công cụ hoặc thư viện hỗ trợ việc versioning DB như dbUp, RoundhousE, các sản phẩm của Red Gate…, hầu hết đều có chung tư tưởng trên, và có thể có thêm một số chức năng khác như:

  • Restore DB. Khởi tạo hoàn toàn một CSDL từ các scripts là điều tuyệt vời khi chúng ta có bộ lịch sử đầy đủ; tuy nhiên, quá trình này thường tốn khá nhiều thời gian thực hiện. Cách làm tốt hơn là nhóm phát triển “chốt” một CSDL ổn định, được coi là điểm bắt đầu và chỉ quản lý những thay đổi bắt đầu từ phiên bản này. Sau một khoảng thời gian, một phiên bản ổn định khác có thể được thay thế. Tính năng này nhằm giảm bớt thời gian khởi tạo CSDL nguyên thuỷ.
  • Có thể roll-back khi gặp lỗi. Khi tạo một script cho sự thay đổi, lập trình viên cũng tạo một “roll-back” script. Khi gặp lỗi, công cụ này sẽ thực hiện những roll-back script này theo thứ tự ngược lại giúp CSDL quay lại trạng thái trước khi bị thay đổi. VD: script ALTER TABLE ADD COLUMN Address NVARCHAR(50); có roll-back script là ALTER TABLE Customer DROP COLUMN Address;
  • Sử dụng chính CSDL hiện có lưu trữ sự thay đổi.
  • Tích hợp với những công cụ CI, CD. Đây là một chức năng tuyệt vời khiến việc tích hợp, triển khai chỉ với một nút nhấn; cũng như đảm bảo không gây sai sót bởi con người (rất dễ xảy ra) ảnh hưởng tới môi trường thật.

DB versioning là vấn đề đặc biệt quan trọng và khá nhức đầu trong việc triển khai CD. Tôi sẽ trở lại vấn đề này với một hướng dẫn cụ thể trong bài viết sau.

1,506 total views, 2 views today

Following my post about database versioning (Vietnamese version), this post shows one solution to handle this issue in real life. Let’s start with .NET project and SQL Server for an example as they are simpler and wide-range use.

Solution

As I mentioned in the previous post, the DB versioning should run as soon as possible during the application startup. And of course, during the build is better at least for 3 reasons:

  • It could save time
  • The conflicts (or failure) could be detected soon.
  • It makes sure the test codes run properly as they may require the latest DB.

For .NET project, RoundhousE seems a good lightweight solution, at least it’s free compared to the features that RedGate provides. But unfortunately, its document isn’t good enough for the newbie and it just focus on the migration stage. So my post could show you an easier way. But please note that it’s a getting started guide because RoundhousE has a lot of features that you could use.

Hand-on

Step by step guideline:

Setup

In your .NET solution, create a library .NET project, name it DbVersioning.{Your_DB_name}, target to .NET 3.5. In this example, I named it DbVersioning.MusicStore, you could replace the MusicStore by your DB name in all the settings below.

All the following steps are in DbVersioning.MusicStore project:

Install the RoundhousE MSBuild Nuget package. It may create some files automatically. 2 files that we should take care are DBDeploy_MSBuild.proj and sample.xml with the information of DB that RoundhousE will work with.

Create folder lib and copy these files to it: log4net.dll, roundhouse.dll, roundhouse.task.dll. You could find these files in packages folder of this solution with the right target .NET version. Make them always copy to output directory.

Create folder db and its appropriate sub-folders. You could find the folder names and their purposes here.

Unload the DbVersioning.MusicStore project and add these lines to DbVersioning.MusicStore.csproj file. Please change your DB name.

Update the information in DBDeploy_MSBuild.proj. Some important fields are: DBServer, DBName, DBConnectionString

Add file _BuildInfo.xml as

Please replace by your project information.

Make sure DbVersioning.MusicStore.csproj will be built. (Right click on your solution -> Properties -> Configuration Properties)

Build your solution. If you got an error about unable to load log4net assembly, you should copy these files in lib folder in my sample project on Github and restart your machine, everything will work well 🙂

Use

Since now, whenever you update the DB, you need:

  • Script your changes to files and add them to db\alterDatabase folder. In my opinion, each change should be in separated .sql file and named in order such as 0005_AddCategoryTable.sql, 0006_AddProductTableWithConstraints.sql
  • Update revision / version in _BuildInfo.xml file. It of course is larger than current numbers.

Known issues

There are some issues that you could meet with RoundhousE:

  • .NET version. RoundhousE MSBuild package is still in .NET 3.5 and no longer updated from 2013. But it runs well right now and you could download and recompile it from source at Github.
  • It might use older version of log4net.
  • RoundhousE will create some tables under RoundhousE schema, so it requires the appropriate privilege login in connection string.

You could find my sample project here.

It’s unnecessary to create another project DbVersioning.MusicStore as you can do it in the main project. But in my opinion, it should be better with a dedicated project for DB versioning as we can easy to detect the changes. And because RoundhousE uses .NET 3.5, it should be in another project to don’t affect to your project settings.

2,433 total views, no views today

Every developers know the importance of source code versioning and how to use SVN, Git etc to achieve but DB versioning seems unfamiliar and more difficult to most of them. But in my opinion, DB versioning is as important as source code versioning, especially when you are working in an Agile project where whole team is responsible to the environment. In this post, I try to describe the common issue with DB and how to solve this.

Problem

Most of software use DB now and of course, it need to be changed during the development and shared with the modules or whole system. And its problem is exact the same as source code where everybody works in only 1 source code repository at the same time:

  • When a developer makes a DB change, how can others aware it? It seems so easy with the source code with SVN or Git because they support the check-in, check-out. So before committing the code, a developer can view the log, see the code changes by other’s check-in.
  • When conflicts occur, how can developers aware and resolve them?

Why do you rarely care?

Let start with the traditional software development process, where the DB:

  • is built in the design stage
  • and it’s mostly fixed during the development.
  • and it’s taken care by a deployment person or team who normally put in charged of environment configuration.

So the developer doesn’t need to care about this. But in the Agile project, its design is unfixed as we may has increments, code or structure refactoring, and the DB schema is often updated sprint by sprint.

What do you usually handle? And their issues?

Follow my experience, there 2 normal ways we are using to handle this problem:

  • Shared environment. The easiest (and sometimes it is the best way) is using shared environment. For example, a developer works on his checked-out source code individually but query on a single shared DB server. It seems a good way but still facing some issues:
    • Performance. Normally it speeds is not as good as using local environment because the shared DB needs to serve all developers’ uses while the local one is more specific on the current module. And it may affect to the productivity.
    • Revision. It so hard to manage the DB revision because it need to go parallel with source code revision where we sometimes roll source code back to the good one. One solution is storing all the DB backup for each commit, but it takes a large amount of storage.
  • Scripting the changes. Whenever we have change on DB, script it and apply to all developer locals. It is the good way to reduce the storage and make easier to version the change. But how team members aware that the scripts are updated and resolve the conflicts?

Solution

DB versioning control

DB versioning control is an approach to keep track the changes on DB schema and data in a shared repository to make sure that team can aware and apply these changes easier.

How does it work?

There are some ways to implement the DB versioning but the most popular one is versioning DB by versioning its change scripts. So the DB versioning problem becomes the source code versioning problem that is solved really good right now.

The details of DB versioning implementation are vary project by project but the key points usually are:

  • DB changes are built by scripts. To reduce the storage and easy to update, we should script DB to files and arrange them in a good structure.
  • DB is under the source control. After scripting DB to files, it makes sense to keep them under the source control because they are the source code now.
  • The DB changes are verified during the project build or application start-up. It makes the developers aware changes that need to be applied. In my opinion, the verification should occur as soon as possible to avoid wasted time. The application start should be failed if the changes haven’t been applied. But it’s better with the failure during the build stage.
  • The DB versions are stored in the DB itself. It’s a good approach to keep the solution simple by for example, a table that stores the run history.

Better solution?

The purpose and a good approach is described above, but there are various specific solutions from project to project. I will have another post to describe an hand-on implementation in a specific project later but you should try to go further by this start as I think DB versioning is very important in Agile projects.

Reference: http://www.infoq.com/articles/db-versioning-scripts

318 total views, no views today