Skip to content

Commit ba4f6e0

Browse files
committed
Clean up upgrade-zodb-to-python3.md
1 parent ad0ebcb commit ba4f6e0

File tree

1 file changed

+117
-99
lines changed

1 file changed

+117
-99
lines changed

docs/backend/upgrading/version-specific-migration/upgrade-zodb-to-python3.md

Lines changed: 117 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ myst:
44
"description": "Upgrading a ZODB from Python 2 to Python 3"
55
"property=og:description": "Upgrading a ZODB from Python 2 to Python 3"
66
"property=og:title": "Upgrading a ZODB from Python 2 to Python 3"
7-
"keywords": "Upgrading, ZODB, Python 3"
7+
"keywords": "Upgrading, ZODB, Python 2, Python 3, ZODB, zodbupdate"
88
---
99

1010
(migrate-zodb-to-python3-label)=
@@ -15,49 +15,54 @@ myst:
1515
The process described here was successfully applied to several projects with its databases.
1616
Anyway, this is still work in progress.
1717
Any help to make this document better is appreciated.
18-
To continue with documenting the process or help improve the involved scripts/tools please have a look at the following resources:
18+
To continue with documenting the process, or help improve the involved scripts and tools. please have a look at the following resources:
1919
20-
- Provide Migration-Story for ZODB with Plone from Python 2 to 3: <https://github.com/plone/Products.CMFPlone/issues/2525>
21-
- Documentation on setting up an environment to test the migration:
22-
<https://github.com/frisi/coredev52multipy/tree/zodbupdate>
20+
- Provide Migration-Story for ZODB with Plone from Python 2 to 3: https://github.com/plone/Products.CMFPlone/issues/2525
21+
- Documentation on setting up an environment to test the migration:
22+
https://github.com/frisi/coredev52multipy/tree/zodbupdate
2323
```
2424

2525
Plone 5.2 can be run on Python 2 and Python 3.
26-
For new projects you can start with a Python 3 with a fresh database.
26+
For new projects, you can start with Python 3 with a fresh database.
2727
To use an existing project in Python 3 though, you need to migrate your existing database first.
2828
This section explains how to do that.
2929

30-
ZODB itself is compatible with Python 3 but a DB created in Python 2.7 cannot be used in Python 3 without modifying it before.
31-
(See [Why do I have to migrate my database?] for technical background).
30+
ZODB itself is compatible with Python 3, but a database created in Python 2.7 cannot be used in Python 3 without modifying it.
31+
See {ref}`why-do-i-have-to-migrate-my-database-label` for technical background.
3232

33-
## Database Upgrade Procedure
3433

35-
In short you need to follow these steps to migrate your database:
34+
## Database upgrade procedure
3635

37-
01. Upgrade your site to Plone 5.2 running on Python 2 first.
38-
(see {doc}`upgrade-to-52`)
39-
02. Make sure your code and all add-ons that you use work in Python 3.
40-
(see {doc}`upgrade-to-python3`)
41-
03. Backup your database!
42-
04. **Pack** your database to **0 days** (`zodbupdate` will not update your database history and will leave old objects in place and you will not be able to pack your database in the future).
43-
05. In your old buildout under Python 2, verify your database integrity using {py:mod}`zodbverify`.
36+
Follow these steps to migrate your database.
37+
38+
1. Upgrade your site to Plone 5.2 running on Python 2 first.
39+
See {doc}`upgrade-to-52`.
40+
2. Make sure your code and all add-ons that you use work in Python 3.
41+
See {doc}`upgrade-to-python3`.
42+
3. Backup your database!
43+
4. _Pack_ your database to _0 days_.
44+
`zodbupdate` will not update your database history, will leave old objects in place, and you will not be able to pack your database in the future.
45+
5. In your old buildout under Python 2, verify your database integrity using {py:mod}`zodbverify`.
4446
Solve integrity problems, if there are any.
45-
06. Prepare your buildout for migrating the database to Python 3
46-
07. Do **not** start the instance.
47-
08. Migrate your database using {py:mod}`zodbupdate`
48-
09. Verify your database integrity using {py:mod}`zodbverify`
47+
6. Prepare your buildout for migrating the database to Python 3.
48+
7. Do _not_ start the instance.
49+
8. Migrate your database using {py:mod}`zodbupdate`.
50+
9. Verify your database integrity using {py:mod}`zodbverify`.
4951
If there are any problems, solve them and redo the migration.
5052
10. Start the instance.
51-
11. Manually check if all works as expected
53+
11. Manually check if all works as expected.
54+
55+
56+
(why-do-i-have-to-migrate-my-database-label)=
5257

53-
## Why Do I Have To Migrate My Database?
58+
## Why do I have to migrate my database?
5459

5560
To understand the problem that arises when migrating a ZODB from Python 2 to Python 3,
5661
this [introduction](https://blog.gocept.com/2018/06/07/migrate-a-zope-zodb-data-fs-to-python-3/) and the following example will help.
5762

58-
When pickling an object the datatypes and values are stored.
63+
When pickling an object, the data types and values are stored.
5964

60-
In Python 2 strings get STRING, and Unicode gets UNICODE
65+
In Python 2, strings get `STRING`, and Unicode gets `UNICODE`.
6166

6267
```console
6368
$ python2
@@ -89,7 +94,7 @@ Python 2.7.14 (default, Sep 23 2017, 22:06:14)
8994
highest protocol among opcodes = 0
9095
```
9196

92-
Python 3 does not allow non-ascii characters in bytes and the pickle declares the byte string as SHORT_BINBYTES and the string (py2 unicode) as BINUNICODE
97+
Python 3 does not allow non-ASCII characters in bytes, the pickle declares the byte string as `SHORT_BINBYTES` and the string (Python 2 Unicode) as `BINUNICODE`.
9398

9499
```console
95100
$ python3
@@ -123,8 +128,8 @@ SyntaxError: bytes can only contain ASCII literal characters.
123128
highest protocol among opcodes = 3
124129
```
125130

126-
Python 3 will wrongly interpret a pickle created with Python 2 that contains non-ascii characters in a field declared with OPTCODE `STRING`.
127-
In that case we may end up with a `UnicodeDecodeError` for this pickle in `ZODB.serialize`
131+
Python 3 will wrongly interpret a pickle created with Python 2 that contains non-ASCII characters in a field declared with `OPTCODE` `STRING`.
132+
In that case, we may end up with a `UnicodeDecodeError` for this pickle in `ZODB.serialize`.
128133

129134
```console
130135
$ python3
@@ -134,7 +139,7 @@ Traceback (most recent call last):
134139
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)
135140
```
136141

137-
Or when UTF-8 encoded byte-strings are interpreted as Unicode we do not get an error but mangled non-ascii characters:
142+
Or when UTF-8 encoded byte strings are interpreted as Unicode, we do not get an error, but mangled non-ASCII characters:
138143

139144
```console
140145
$ python3
@@ -144,24 +149,26 @@ $ python3
144149
Ãmläut
145150
```
146151

147-
## How zodbupdate solves the problem
148152

149-
{py:mod}`zodbupdate` loads the ZODB and iterates on a low-level without actually loading the pickle over all pickle-data.
153+
## How `zodbupdate` solves the problem
154+
155+
{py:mod}`zodbupdate` loads the ZODB and iterates on a low level without actually loading the pickle over all pickle data.
150156

151157
It does:
152158

153-
- Update magic marker to indicate the new format of the database.
154-
- Rename classes and modules with different locations.
155-
- Convert bytes to string or keep binary (according to rule-mappings).
156-
- Handle encoding-problems while doing bytes to string conversion and provide fallbacks for mixed encodings in DB.
159+
- Update magic marker to indicate the new format of the database.
160+
- Rename classes and modules with different locations.
161+
- Convert bytes to string or keep binary (according to rule mappings).
162+
- Handle encoding problems while doing bytes-to-string conversion, and provide fallbacks for mixed encodings in the database.
163+
157164

158-
## Prepare Your Buildout For Migrating The Database To Python 3
165+
## Prepare your buildout for migrating the database to Python 3
159166

160-
You need to add {py:mod}`zodbverify` to your Python 2 buildouts ``` eggs = `` variable in the ``[instance] ``` section.
167+
You need to add {py:mod}`zodbverify` to your Python 2 buildout's `eggs =` variable in the `[instance]` section.
161168

162169
You need to add the package {py:mod}`zodbupdate` and {py:mod}`zodbverify` to your Python 3 buildout.
163170

164-
Depending on your buildout this could look like this:
171+
Depending on your buildout, this could look like the following.
165172

166173
```ini
167174
[buildout]
@@ -186,16 +193,16 @@ eggs =
186193
zodbupdate = git https://github.com/zopefoundation/zodbupdate.git pushurl[email protected]:zopefoundation/zodbupdate.git branch=master
187194
```
188195

189-
This adds a new buildout-part `zodbupdate`.
190-
The coredev-buildout already has this part.
196+
This adds a new buildout part `zodbupdate`.
197+
The `coredev` buildout already has this part.
191198

192-
After re-running buildout you will now have a new executable `./bin/zodbupdate`.
199+
After re-running buildout, you will now have a new executable `./bin/zodbupdate`.
193200

194201
````{warning}
195202
Do not try to start Plone in Python 3 with the old database before migrating it!
196-
Trying to that will destroy the database and result in a traceback like this:
203+
Trying to do that will destroy the database and result in a traceback, such as the following.
197204
198-
```
205+
```console
199206
Traceback (most recent call last):
200207
File "/Users/pbauer/workspace/projectx/parts/instance/bin/interpreter", line 279, in <module>
201208
exec(compile(__file__f.read(), __file__, "exec"))
@@ -210,39 +217,40 @@ ZODB.FileStorage.FileStorage.FileStorageFormatError: /Users/pbauer/workspace/pro
210217
```
211218
````
212219

213-
## Verify The Integrity of the Database in Python 2
220+
221+
## Verify the integrity of the database in Python 2
214222

215223
The preflight verification of the database is run on Plone 5.2 in Python 2.
216-
First check if all Python-pickles in the database can be loaded.
217-
In older and grown projects it is possible to have pickles in there pointing to classes long gone in code.
224+
First check if all Python pickles in the database can be loaded.
225+
In older and mature projects, it is possible to have pickles in there pointing to classes long gone from the code.
218226
Those may cause problems later.
219227

220228
Call `./bin/instance zodbverify` in your Python 2.7 setup.
221-
If a problem pops up there is a debug mode with additional parameter `-D`, resulting in PDB with the pickle-data and a decompiled pickle in place to gather information about the source of the problem.
229+
If a problem pops up, there is a debug mode with additional parameter `-D`, resulting in PDB with the pickle-data and a decompiled pickle in place to gather information about the source of the problem.
222230
This enables solving the problem by either adding a stub class in the code or by deleting the object in the ZODB.
223231

224-
## Migrate Database using zodbupdate
232+
233+
## Migrate database using `zodbupdate`
225234

226235
The migration of the database is run on Plone 5.2 in Python 3.
227-
It is expected to work equally in Python 3.6 and 3.7.
236+
It is expected to work the same in Python 3.6, 3.7, or 3.8.
228237

229-
Run the migration by
238+
Run the migration with appropriate arguments.
230239

231-
- passing the operation to undertake (`convert-py3`),
232-
- the location of the database,
233-
- the encoding expected and
234-
- optional, encoding fallbacks if the database contains mixed encodings.
240+
- The operation to perform, `convert-py3`.
241+
- The location of the database.
242+
- The encoding expected.
243+
- Optionally, encoding fallbacks, if the database contains mixed encodings.
235244

236-
```console
245+
```shell
237246
./bin/zodbupdate --convert-py3 --file=var/filestorage/Data.fs --encoding utf8 --encoding-fallback latin1
238247
```
239248

240-
Depending on the size of you database this can take a while.
249+
Depending on the size of your database, this can take a while.
241250

242-
Ideally the output is similar to this:
251+
Ideally the output is similar to the following.
243252

244253
```console
245-
$ ./bin/zodbupdate --convert-py3 --file=var/filestorage/Data.fs --encoding=utf8
246254
Updating magic marker for var/filestorage/Data.fs
247255
Ignoring index for /Users/pbauer/workspace/projectx/var/filestorage/Data.fs
248256
Loaded 2 decode rules from AccessControl:decodes
@@ -253,54 +261,60 @@ Committing changes (#1).
253261
```
254262

255263
```{note}
256-
The blobstorage (holding binary data of files and images) will not be changed or even be read during the migration since the blobs only contain the raw binary data of the file/image.
264+
The blob storage (holding binary data of files and images) will not be changed or even be read during the migration, because the blobs only contain the raw binary data of the file or image.
257265
```
258266

259267
```{note}
260-
The encoding should always be `utf8` and will be used when porting database-entries of classes where no encoding is specified in a `[zodbupdate.decode]` mapping in the package that holds the base-class.
268+
The encoding should always be `utf8`, and will be used when porting database entries of classes where no encoding is specified in a `[zodbupdate.decode]` mapping in the package that holds the base class.
261269
```
262270

263271
```{note}
264272
The encoding fallback is optional and should not be provided by default.
265-
If a `UnicodeDecodeError` occur, try to find out if the instance was configured with encodings different from `utf8`.
266-
Provides those as encodings as fallback.
267-
If in doubt try `latin1` since this was in former times of Zope the default encoding.
273+
If a `UnicodeDecodeError` occurs, try to find out if the instance was configured with encodings different from `utf8`.
274+
Provide those encodings as the `--encoding-fallback` argument.
275+
If in doubt, try `latin1`, since this was in former times of Zope the default encoding.
268276
```
269277

270-
## Test Migration
271278

272-
You can use the following command to check if all records in the database can be successfully loaded:
279+
## Test migration
273280

274-
```console
281+
You can use the following command to check if all records in the database can be successfully loaded.
282+
283+
```shell
275284
bin/instance zodbverify
276285
```
277286

278-
The output should look like this:
287+
The output should look like the following.
279288

280289
```console
281-
$ ./bin/instance zodbverify
282-
283290
INFO:Zope:Ready to handle requests
284291
INFO:zodbverify:Scanning ZODB...
285292
INFO:zodbverify:Done! Scanned 7781 records. Found 0 records that could not be loaded.
286293
```
287294

288-
Most likely you will have additional log-messages, warnings and even errors.
295+
Most likely you will have additional log messages, warnings, and even errors.
289296

290297
```{note}
291-
You can use the debug-mode with `./bin/instance zodbverify -D` which will drop you in a pdb each time a database-entry cannnot be unpickled so you can inspect it and figure out if that is a real issue or not.
298+
You can use the debug mode with `./bin/instance zodbverify -D`.
299+
This will drop you into a `pdb` session each time a database entry cannnot be unpickled
300+
You can inspect it, and figure out if that is a real issue or not.
292301
293-
Before you start debugging you should read the following section on Troubleshooting because in many cases you can ignore the warnings.
302+
Before you start debugging you should read the following section on {ref}`troubleshooting-zodbverify-label`, because in many cases you can ignore the warnings.
294303
```
295304

305+
306+
(troubleshooting-zodbverify-label)=
307+
296308
## Troubleshooting
297309

298-
### Data.fs.index broken
310+
This section covers common issues when running `zodbverify`.
299311

300-
Delete `Data.fs.index` before migrating or you will get this error during migrating:
312+
313+
### `Data.fs.index` broken
314+
315+
Delete `Data.fs.index` before migrating, or you will get the following error during migrating.
301316

302317
```console
303-
$ ./bin/zodbupdate --convert-py3 --file=var/filestorage/Data.fs --encoding=utf8
304318
Updating magic marker for var/filestorage/Data.fs
305319
loading index
306320
Traceback (most recent call last):
@@ -313,18 +327,20 @@ UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 249: ordina
313327

314328
This error can be safely ignored.
315329

316-
### Search/ Catalog raises errors
317330

318-
If searches are failing and are raising errors, go to the ZMI of your Plone Site root.
319-
Select the `portal_catalog` and click on the `Advanced` tab.
320-
Select `Clear and Rebuild`.
321-
This may take a while!
331+
### Search/Catalog raises errors
322332

323-
### ModuleNotFoundError: No module named PloneLanguageTool
333+
If searches are failing and are raising errors, go to the ZMI of your Plone site root.
334+
Select the {guilabel}`portal_catalog`, and click on the {guilabel}`Advanced` tab.
335+
Select {guilabel}`Clear and Rebuild`.
336+
This may take a while.
324337

325-
There were cases when the migration aborted with a import-error like this:
326338

327-
```
339+
### `ModuleNotFoundError: No module named PloneLanguageTool`
340+
341+
There were cases when the migration aborted with an import error such as the following.
342+
343+
```console
328344
An error occured
329345
Traceback (most recent call last):
330346
File "/Users/pbauer/.cache/buildout/eggs/plone.app.upgrade-2.0.22-py3.7.egg/plone/app/upgrade/__init__.py", line 120, in <module>
@@ -356,7 +372,8 @@ Traceback (most recent call last):
356372
ModuleNotFoundError: No module named 'PloneLanguageTool'
357373
```
358374

359-
To work around this comment out the lines offending lines in `plone/app/upgrade/__init__.py` (do not forget to uncomment them after the migration!)
375+
To work around this, comment out the lines offending lines in `plone/app/upgrade/__init__.py`.
376+
Remember to uncomment them after the migration!
360377

361378
```python
362379
# try:
@@ -371,17 +388,17 @@ To work around this comment out the lines offending lines in `plone/app/upgrade/
371388
# ).PloneLanguageTool.LanguageTool.LanguageTool
372389
```
373390

374-
### Migration Logs Errors And Warnings
391+
### Migration logs errors and warnings
375392

376-
If there are log-messages during the migration or during `zodbverify` that does not necessarily mean that the migration did not work or that your database is broken.
377-
For example if you migrated from Plone 4 to Plone 5 and then from Archetypes to Dexterity it is very likely that items in the database cannot be loaded because packages like `Products.Archetypes`, `plone.app.blob` or `plone.app.imaging` are not available.
378-
These items are most likely remains that were not removed properly but are not used.
379-
If your site otherwise works fine you can choose to ignore these issues.
393+
If there are log messages during the migration or during `zodbverify`, that does not necessarily mean the migration did not work, or that your database is broken.
394+
For example, if you migrated from Plone 4 to Plone 5, and then from Archetypes to Dexterity, it is very likely that items in the database cannot be loaded, because packages such as `Products.Archetypes`, `plone.app.blob`, or `plone.app.imaging` are not available.
395+
These items most likely remain, and were not removed properly and are not used.
396+
If your site otherwise works fine, you can choose to ignore these issues.
380397

381-
Here is the output of a migration start started in Plone 4 with Archetypes.
398+
Here is the output of a migration started in Plone 4 with Archetypes.
382399
The site still works nicely in Plone 5.2 on Python 3.7 despite the warnings and errors:
383400

384-
```
401+
```console
385402
Updating magic marker for var/filestorage/Data.fs
386403
Loaded 2 decode rules from AccessControl:decodes
387404
Loaded 12 decode rules from OFS:decodes
@@ -459,16 +476,17 @@ Found new rules: {
459476
}
460477
```
461478

462-
## Downtime
463479

464-
Some thoughts on doing upgrades without downtime that came up in a Hangout during a coding sprint in October 2018:
480+
## Eliminating downtime
481+
482+
You can try the following to have an upgrade without downtime.
465483

466-
- You can try to leverage the ZRS replication protocol, where the secondary server has the converted data.
467-
It would probably be a trivial change to ZRS to get this to work.
468-
- For Relstorage there is a ZRS equivalent for Relstorage: <http://www.newtdb.org/en/latest/topics/following.html>
484+
- You can try to leverage the ZRS replication protocol, where the secondary server has the converted data.
485+
It would probably be a trivial change to ZRS to get this to work.
486+
- There is a [ZRS equivalent for Relstorage](https://www.newtdb.org/en/latest/topics/following.html).
469487

470-
## Further Reading
471488

472-
The Zope Documentation contains a [section about ZODB migration](https://zope.readthedocs.io/en/latest/zope4/migration/zodb.html)
489+
## Further reading
473490

474-
ZODB Database debugging: https://www.starzel.de/blog/zodb-debugging
491+
- The Zope Documentation contains a [section about ZODB migration](https://zope.readthedocs.io/en/latest/migrations/index.html).
492+
- [ZODB Database debugging](https://www.starzel.de/blog/zodb-debugging)

0 commit comments

Comments
 (0)