Kiril,
Each migration is performed in its own transaction, and the transaction is rolled back on failure. However, the only supported database that completely rolls back DDL is PostgreSQL. Which means for most users, it's possible to end up with a database that is partially migrated.
I have two databases for each project: projectname_test and projectname_development. _test is used for automated tests and is generally empty (only an empty schema). I develop new migrations against this _test database, and when something goes wrong I correct the problem and perform a db-migration:reset, which drops the db, creates a new one and then runs all of the migrations. Once it's good, I run it on my _development database which contains some data, then I check the migration into version control. This works well and nearly eliminates the case where migrations fail, so I don't have to worry too much about getting a database into a partially migrated state.
The migration framework does not lock a database when running. I'm not sure how to do that, but it's there's an easy generic way to do it it's something we could consider adding.
Cheers,
Christian