Reporting on the Progress of a Flyway Database Development Project
It is tempting to get a bit vague when reporting on the progress of a database development project. In fact, though, I'm an enthusiast for copious reporting. It reveals, quickly, what is and isn't going on for the team. If you make all the documentation available on an internal Wiki, then it also makes it more difficult for anyone in the deployment chain to use the hackneyed defense 'You never told me that you were doing x' (where x can stand for almost every database change). As a bonus, I've always found that the quickest way to subdue senior managers is to drown them in a tsunami of reporting. It is the project leader's equivalent of the Vulcan Nerve Grip.
When I say that I am a reporting enthusiast, I should add the rider 'only if it can be automated'. When required to write conventional documentation I am, like all developers, gripped with an extraordinary and inexplicable torpor, as if drifting off into semi-consciousness. It is only the idea that I can automate the task that gives me the necessary mental stamina to do it.
How to plot the progress of a Flyway project
How can Flyway help the busy database developer keep track of development progress? Well, it has the Flyway Schema History table. For each database managed by Flyway, this table keeps a record of what happened and when. If you maintain a strict one-to-one correspondence between the folder(s) of migration files and the branch database (e.g., Production, Develop, Bugfix, and so on) then you have, in the Flyway Schema History table, a complete history of changes and who made them.
We can extract this data and present it in a Gantt Chart, for example:

You will notice that I've plotted the time it took to develop and apply each migration. It is the elapsed time between the previous and current migration (i.e., the "end" time for V1.1.1 migration is the "start" time for the v1.1.2 migration, and so on).
I've also shown who installed each version. The name that appears as a resource ('Phil Factor' in my case) is taken from the installedBy parameter, not the database user_ID. Unfortunately, this project was done just by me so, we can't do much with the 'resources' information. If you want to keep a check on resources, generally, you'll need to be sure that each user has this value set appropriately, in the flyway.conf file within their user directory.
I produced this Gantt chart by generating it in SQL (I'll show the code shortly). It extracts the data from the Flyway History Table and then transforms it into PlantUML (PUML) code. The resulting source (.puml) for the above Gantt chart is as follows:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
@startgantt
skinparam LegendBorderRoundCorner 2
skinparam LegendBorderThickness 1
skinparam LegendBorderColor silver
skinparam LegendBackgroundColor white
skinparam LegendFontSize 11
printscale weekly
saturday are closed
sunday are closed
title Gantt Chart for version 1.1.11
legend top left
Database: MyDatabase
Server: MyServer
RDBMS: sqlserver
Flyway Version: 1.1.11
endlegend
printscale weekly
saturday are closed
sunday are closed
Project starts 2021-01-03
[1.1.1 - Initial Build] on {Phil Factor} starts 2021-01-03
[1.1.2 - Pubs Original Data] on {Phil Factor} starts 2021-01-05
[1.1.1 - Initial Build] ends 2021-01-05
[1.1.3 - UseNVarcharetc] on {Phil Factor} starts 2021-01-08
[1.1.2 - Pubs Original Data] ends 2021-01-08
[1.1.4 - RenameConstraintsAdd tables] on {Phil Factor} starts 2021-01-12
[1.1.3 - UseNVarcharetc] ends 2021-01-12
[1.1.5 - Add New Data] on {Phil Factor} starts 2021-01-20
[1.1.4 - RenameConstraintsAdd tables] ends 2021-01-20
[1.1.6 - Add Tags] on {Phil Factor} starts 2021-01-25
[1.1.5 - Add New Data] ends 2021-01-25
[1.1.7 - Add Indexes] on {Phil Factor} starts 2021-02-04
[1.1.6 - Add Tags] ends 2021-02-04
[1.1.8 - AddEditions] on {Phil Factor} starts 2021-02-15
[1.1.7 - Add Indexes] ends 2021-02-15
[1.1.9 - AddconditionalVersion] on {Phil Factor} starts 2021-03-01
[1.1.8 - AddEditions] ends 2021-03-01
[1.1.10 - AddAddressesPhonesEtc] on {Phil Factor} starts 2021-03-10
[1.1.9 - AddconditionalVersion] ends 2021-03-10
[1.1.11 - AddProcedureWithTest] on {Phil Factor} starts 2021-03-12
[1.1.10 - AddAddressesPhonesEtc] ends 2021-03-12
@endgantt
|
It is best to review the Gantt Diagram page of the PlantUML site for the explanation of this script. They are adding useful extensions regularly.
Using this script as the source, we can then draw the chart using plantumlc.exe, or by using a PUML editor such as ATOM. There are online resources to help with the latter, but I just use plantumlc.exe, especially as this allows me to decide the format, such as PDF.
Generating the Gantt Chart
There are two main approaches to getting to this information and turning it into a PUML script like this: you can use the RDBMS-specific SQL to query the table, or you can use the slower RDBMS-independent approach of using the Flyway info command to get the information (Flyway does additional checks) and presenting it in JSON form for processing.
Using SQL to generate the PUML
We'll start with the RDBMS-specific way. This query is for SQL Server.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
/* we read the Flyway Schema History into a table variable so we can then do a line--by-line select with
a guarantee of doing it in the order of the primary key */
DECLARE @FlywaySchemaTable TABLE
([installed_rank] [INT] NOT NULL PRIMARY KEY,
[version] [NVARCHAR](50) NULL,
[description] [NVARCHAR](200) NULL,
[installed_by] [NVARCHAR](100) NOT NULL,
[installed_on] [DATETIME] NOT NULL)
/* now read in the table */
INSERT INTO @FlywaySchemaTable
(Installed_rank, version,
installed_by, installed_on, description )
--I've added the placeholders in case you want to execute this in a callback
SELECT fsh.installed_rank, version, installed_by, installed_on, description
FROM dbo.flyway_schema_history FSH --FROM $(flyway:defaultSchema).$(flyway:TABLE)
INNER JOIN
(SELECT Max (installed_rank) AS installed_rank
FROM dbo.flyway_schema_history --FROM $(flyway:defaultSchema).$(flyway:TABLE)
WHERE
success = 1 AND type = 'SQL' AND version IS NOT NULL
GROUP BY version) f
ON f.installed_rank = fSH.installed_rank
ORDER BY fSH.installed_rank;
/* now we calculate the version. This is slightly complicated by the
possibility that you've done an UNDO. I've added the placeholders
in case you want to execute this in a callback */
DECLARE @Version [NVARCHAR](50) =
(SELECT TOP 1 [version] --we need to find the greatest successful version.
FROM dbo.flyway_schema_history --FROM $(flyway:defaultSchema).$(flyway:TABLE) --
WHERE
installed_rank =
(SELECT Max (installed_rank)
FROM PubsSix.dbo.flyway_schema_history --FROM $(flyway:defaultSchema).$(flyway:TABLE)
WHERE success = 1));
DECLARE @PlantUMLCode NVARCHAR(MAX)='@startgantt
skinparam LegendBorderRoundCorner 2
skinparam LegendBorderThickness 1
skinparam LegendBorderColor silver
skinparam LegendBackgroundColor white
skinparam LegendFontSize 11
printscale weekly
saturday are closed
sunday are closed
title Gantt Chart for version '+@Version+'
legend top left
Database: '+Db_Name()+'
Server: '+@@ServerName+'
RDBMS: sqlserver
Flyway Version: '+@Version+'
endlegend
printscale weekly
saturday are closed
sunday are closed
'
DECLARE @PreviousDescription NVARCHAR(100)
--used to temporarily hold the previous description
SELECT @PlantUMLCode=@PlantUMLCode +
CASE WHEN @PreviousDescription IS NULL THEN 'Project starts '+Convert(NCHAR(11),Convert(DATETIME2,Installed_on,112)) +'
' ELSE '' END+
'['+version+' - '+description+'] on {'+[installed_by]+'} starts '+ Convert(NCHAR(11),Convert(DATETIME2,Installed_on,112))+'
'
+ CASE WHEN @PreviousDescription IS NOT NULL THEN '['+@Previousdescription+'] ends '+Convert(NCHAR(11),Convert(DATETIME2,Installed_on,112))+'
' ELSE '' END,
@PreviousDescription = version+' - '+description
FROM @FlywaySchemaTable WHERE version IS NOT null
SELECT @PlantUMLCode=@PlantUMLCode+'@endgantt'
SELECT @PlantUMLCode
|
This will produce the PUML script I showed earlier. You should use 'Results to text' (Ctrl T) in SSMS if you wish to generate the PUML directly from the query. For the sake of this article, you can then use the PlantUML online demo server to render it into the Gantt chart. Simply paste in the code and hit "Submit".
Using PowerShell to generate the PUML
The alternative RDBMS-independent approach is to execute a Flyway Info command that specifies JSON output. We can then use a script to create the PUML. In the interests of avoiding repetition, I'll leave it to you as how you get those Flyway args into a hashtable but I've already covered this in several articles. Note that this code actually calls Flyway so using it in certain callbacks will cause blocking, but it is safe to do after the end of a 'run' (e.g. 'afterVersioned’), because the transaction for the migration will have ended.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
$report = Flyway info -outputType=json @FlywayArgs | convertFrom-json
#get a JSON report of the history.
if ($report.error -ne $null) #if an error was reported by Flyway
{ #if an error (usually bad parameters) error out.
$report.error | foreach { Write-error "$($_.errorCode): $($_.message)" }
}
if ($report.allSchemasEmpty) #if it is an empty database
{ write-verbose "all schemas are empty. No version has been created here" }
else
{ <# we first create the PlantUML Gantt header as a single string #>
$PUML=@"
@startgantt
skinparam LegendBorderRoundCorner 2
skinparam LegendBorderThickness 1
skinparam LegendBorderColor silver
skinparam LegendBackgroundColor white
skinparam LegendFontSize 11
printscale weekly
saturday are closed
sunday are closed
title Gantt Chart for version $($Report.schemaVersion)
legend top left
Database: $($Report.database)
Server: $server
RDBMS: $rdbms
Flyway Version: $($Report.flywayVersion)
endlegend
"@
<# now we add to the header each line that represents a line in the gantt chart. To make
things more interesting, we have to terminate the period of the previous line if there is one
at the same time so that the period for the next version starts when the previous one ends #>
$puml+= $Report.migrations| where {$_.version -ne '' -and $_.state -in @('Success','Future')}|
group version | Sort-Object -Property @{Expression = {[version]$_.version}; Descending = $true}|
%{$theGroup=$_;
[PSCustomObject]@{
version = $_.name;
installedBy=$_.Group.installedBy|select -First 1;
description=$_.Group.description|select -First 1;
installedOnUTC = $_.Group.installedOnUTC|sort-object|select -Last 1
}}|sort-object -Property installedOnUTC|
foreach -Begin {$oldmigration=$null} -Process {
#first, do a 'Project starts, taking it back to the start of the initial week.
$migration=$_
if ($oldmigration -eq $null) {
$StartDate=[datetime]$migration.installedOnUTC
"Project starts $($StartDate.AddDays(1 - $StartDate.DayOfWeek.value__).ToString("yyyy-MM-dd"))
"}
"[$($migration.version) - $($migration.Description)] on {$($migration.installedBy)} starts $($migration.installedOnUTC.Remove(10))
"
if ($oldmigration -ne $null)
{"[$($oldmigration.version) - $($oldmigration.Description)] ends $($oldmigration.installedOnUTC.Remove(10))
"}
$oldmigration=$_
} -End {'@endgantt'} #we end the chart with this
# we create a filename to write the result into, and the same filename for the graphic
$filename="$($env:tmp)\$($Report.database)-v$($Report.schemaVersion)--$(Get-random -maximum 99).puml"
[IO.File]::WriteAllLines($filename, $puml) # It must be UTF8!!!
<# I've chosen to output a svg image because it looks better in a website but you can use
a number of different report types #>
plantumlc -tsvg $filename
}
|
Conclusions
A Gantt chart showing the migration run, and who did what, is a good place to start with reporting. I like to add a report on what changed at every version, but I've already shown how to do that in Reporting on Changes Made by Flyway Migration Scripts. In another article, I’ll show how to embed this code in a PowerShell script and in a Callback for Flyway Teams.
PlantUML is useful because it is well-maintained by enthusiasts and does ER Diagramming, documenting JSON, and a whole range of other useful tasks as well as Gantt charts. I haven't mentioned graphs. These are easily generated too and are very handy for the more complex database performance tuning work because it makes it much easier to detect performance changes that affect more than one metric. PlantUML doesn't support this but there are many graphing packages that do.






Loading comments...