Migrating DAML ledgers¶
Building migration projects¶
When we want to replace a DAML package foo-1.0.0
that is deployed on a DAML ledger with an
updated version, say foo-2.0.0
, we need to migrate all the existing contracts that are active
and whose templates are defined in either foo-1.0.0
or whose template definition depends directly
or indirectly on data defined in foo-1.0.0
. To help a DAML deployment with this task, the DAML
assistant offers the command daml migrate
.
The migrate
command takes as inputs the package foo-1.0.0
and its new version foo-2.0.0
and creates a new project containing generated code to migrate contracts from foo-1.0.0
to
foo-2.0.0
. Run daml migrate --help
to see its full usage description:
Usage: daml migrate TARGET_PATH FROM_PATH TO_PATH
Available options:
TARGET_PATH Path where the new project should be located
FROM_PATH Path to the dar-package from which to migrate from
TO_PATH Path to the dar-package to which to migrate to
-h,--help Show this help text
For example, to create a migration project from foo-1.0.0
to foo-2.0.0
run
daml migrate foo-upgrade-2.0.0 foo-1.0.0/.daml/dist/foo-1.0.0.dar foo-2.0.0/.daml/dist/foo-2.0.0.dar
This generates a migration project in the directory foo-upgrade-2.0.0
. To build it, change
directory to foo-upgrade-2.0.0
and run
daml build
Writing upgrade/rollback contracts¶
It is important to understand that the daml migrate
command will not always succeed in
generating a migration project that will compile. To understand why, let’s assume that the
foo-1.0.0
consists of a single module:
foo-1.0.0
├── daml
│ └── Foo.daml
├── daml.yaml
└── ui-backend.conf
where the Foo.daml
file contains
daml 1.2
module Foo where
template Foo
with
a : Int
p : Party
where
signatory p
The package foo-2.0.0
contains exactly the same modules, but a new template Bar
has been
added to the Foo
module.
daml 1.2
module Foo where
template Foo
with
a : Int
p : Party
-- t : Text
where
signatory p
template Bar
with
t : Text
p : Party
where
signatory p
If we generate a migration project with daml migrate
as above, the directory contents of the
foo-2.0.0-upgrade/daml
directory is
daml
└── Foo.daml
For every template that was defined in the module Foo
in the foo-1.0.0
package, you will
find two new templates. One to upgrade contract instances of this template to the changed template
defined in the module Foo
in the package foo-2.0.0
and one to rollback the process. Here is
an example:
daml 1.2
module Foo where
import FooA qualified as A
import FooB qualified as B
import DA.Upgrade
template instance FooUpgrade = Upgrade A.Foo B.Foo
template instance FooRollback = Rollback A.Foo B.Foo
instance Convertible A.Foo B.Foo where
convert A.Foo{..} = B.Foo {..}
instance Convertible B.Foo A.Foo where
convert B.Foo{..} = A.Foo {..}
The new template types are defined via a type alias and use generic templates to update or rollback
contract instances defined in the DAML standard library (see DA.Upgrade). The Upgrade
template
offers a choice to input a contract instance defined in foo-1.0.0
and create a new one that has
been converted to a contract instance defined in foo-2.0.0
. The Rollback
template implements
the reversed workflow.
The last line generates a Convertible
instance for the Foo
template of both packages. This
is a manifestation that instances of the Foo
template in foo-1.0.0
can be converted to
instances of the equally named template in foo-2.0.0
.
The migrate
command only generates a suggestion for an upgrade/rollback contract of your data
types. If your upgrade workflow doesn’t match the Upgrade
template provided by the standart
library you can write your own specification on how contracts from the foo-1.0.0
package are
converted to contracts of the foo-2.0.0
package.
The suggested Convertible
instances use the shallow copy mechanism A.Foo{..} = B.Foo{..}
.
This will only work for trivial conversions, where the two data types A.Foo
and B.Foo
are
defined the same in both packages. If they are not isomorphic, the compilation will fail and you
will have to specify how to convert the data types manually. One possibility is to use DA.Generics
to avoid boilerplate code. This is described in the section Using Generics for deep copies.
Deploying the migration¶
Once you’ve succeeded building the foo-upgrade-2.0.0
package you can deploy it on the ledger
together with the foo-2.0.0
package. Optionally you can bundle it with the foo-2.0.0
package
into a single DAML archive by running
daml damlc merge-dars foo-2.0.0/.daml/dist/foo-2.0.0.dar foo-upgrade-2.0.0/.daml/dist/foo-upgrade-2.0.0.dar --package-name foo-2.0.0-with-upgrades.dar
You find more information on how to deploy DAML archive packages here .
After the foo-upgrade-2.0.0
package has been deployed on the ledger, there exists for every
contract defined in foo-1.0.0
a DAML workflow with a choice to upgrade it to foo-2.0.0
or
roll it back.
Using Generics for deep copies¶
When writing Convertible
instances, you will often want to deep copy data types that are
esstentially the same. To avoid boilerplate code, you can use the default methods of the
Convertible
class defined via generic representations of the data types.
Generic representations can be converted when they are isomorphic. That means the corresponding
data types defined in package foo-1.0.0
and foo-2.0.0
have exactly the same shape. For
example the following two data types are isomorphic:
data Either a b = Left a | Right b
data UpDown a b = Up a | Down b
while the following data types are not
data Either a b = Left a | Right b
data Maybe a = Just a | Nothing
damlc
provides the command generate-generic-src
to generate modules containing Generic
instances for your data types. For example
daml damlc generate-generic-src --qualify A --srcdir foo-upgrade-2.0/daml foo-1.0.0/.daml/dist/foo-1.0.0.dar
daml damlc generate-generic-src --qualify B --srcdir foo-upgrade-2.0/daml foo-2.0.0/.daml/dist/foo-2.0.0.dar
generates two modules FooAGenInstances.daml
and FooBGenInstances.daml
in the
foo-upgrade-2.0/daml
directory. This allows us to just use the default Convertible
instances
to do the deep copying:
daml 1.2
module Foo where
import FooA qualified as A
import FooB qualified as B
import FooAGenInstances()
import FooBGenInstances()
import DA.Upgrade
template instance FooUpgrade = Upgrade A.Foo B.Foo
template instance FooRollback = Rollback A.Foo B.Foo
instance Convertible A.Foo B.Foo
instance Convertible B.Foo A.Foo
You can build this upgrade project with
daml build --generated-src
If your data types are not isomorphic and the default methods don’t compile you have to specify the conversion manually, for example:
daml 1.2
module Foo where
import FooA qualified as A
import FooB qualified as B
import DA.Upgrade
template instance FooUpgrade = Upgrade A.Foo B.Foo
template instance FooRollback = Rollback A.Foo B.Foo
instance Convertible A.Foo B.Foo where
convert d = B.Foo with a = d.a; p = d.p; t = "upgraded"
instance Convertible B.Foo A.Foo where
convert d = A.Foo with a = d.a; p = d.p