Slightly changing how Moodle version numbers work

Slightly changing how Moodle version numbers work

by Martin Dougiamas -
Number of replies: 12
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
We're starting to run out of the main version numbers .... 99 increments for the whole life of a stable branch isn't enough.

We had a chat about this last night on Moodle HQ chat with various proposals:

Extend to 14 characters 200802140000
  • The cleanest idea but it exceeds PHP_INT_MAX for 32-bit machines so that can't work sad
Add current date as decimal to the branch date 2007101500.2008021400
  • PHP floats don't support that sort of precision properly. sad
Add smaller floating point increments for branches only 2007101500.001
  • We think this will work as long as we avoid testing any equalities.

So the current plan is:
  1. Keep the same version numbering as now for HEAD (gives us 99 per day which is fine) 2008021599
  2. Increment all stable branches with .001 .002 .003 .004 as required (gives us 1000 changes per stable branch) 2007101500.999
  3. Automatically update $release in all branches with the current date, starting from 1.9, to help bug reports etc eg 1.9 + (2008/02/20)
if you have any comments / ideas /feedback about this, please see MDL-13477.
Average of ratings: -
In reply to Martin Dougiamas

Re: Slightly changing how Moodle version numbers work

by Martín Langhoff -
Are DB/roles updates the main concern? 99 revisions used to be enough, but I guess we want more to trigger roles and new admin settings checks wink

We can use a mix of the current versioning scheme plus "DB patches" scheme. The current scheme has 2 limits we're hitting: one is that 99 limit, the other one is the linearity of it.

I have experience with a system that keep a "list of db patches" applied. Works great when doing branched development (with conflicts sometimes, but only in the "natural" places where you'd expect a conflict). Obviously, that breaks down over time because the list gets unwieldy.

A mixed system of linear "sync points" based on what we have now (that we "freeze" the base of the version at branching time) plus db patches will cover both bases. And perhaps we can have a "lighter" version of the same scheme to "bump" roles/admin updates.

I'll try and write something up over the weekend and post it here for wider review. Personally, I've been hitting my head against the version issue for a while with all the branched devel I do mixed
In reply to Martin Dougiamas

Re: Slightly changing how Moodle version numbers work

by Iñaki Arenaza -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Maybe this is a crazy idea, but there it goes...

I don't know how often this version number is tested (i.e., is it once per page view, 100 times per page view, once per admin page view, etc.).

But if it's just a couple of times per page view (unless we are running the upgrade scripts, of course), we could use the arbitrary precision mathematics extension (bundled with PHP since 4.0.4, with no external requirements), and have as big "integer" (or "float") numbers as we want, even on 32-bit machines (as long as they fit in mdl_config smile)

We just need to specify them as strings and use bccomp() instead of the comparison operators.

We could even handle the transition from integers to strings automagically by checking the type of $version and doing the old stuff when it's an integer and the new stuff when it's and string.

I see there are a few places in the code (outside the upgrade logic) where we directly check $CFG->version against particular version numbers, so we should 'fix' those places too. Maybe a function in moodlelib.php would be in order here to fix the problem once and for all wink.

Saludos. Iñaki.

In reply to Iñaki Arenaza

Re: Slightly changing how Moodle version numbers work

by Martin Dougiamas -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
I did look at that but they don't appear to always be available (ie they are bundled with the source but need to be included during compile time with --enable-bcmath)
In reply to Martin Dougiamas

Re: Slightly changing how Moodle version numbers work

by Iñaki Arenaza -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

While I like the schema MartinL has proposed better, we could use something like this even if we don't use bcmath at all.

As long as we keep the format of the version string 'fixed', say:

yyyymmmdddnn.oooo

where 'nn' is the 'major' revision (e.g., database changes) and 'oooo' is the minor revision (e.g., role updates and things like that), and both of them have always the same 'witdth', the string comparison operator works nicely (the format leads to lexicographical order by itself). We should keep 'nn' at two digits for compatibility with current code, but we can choose as many digits as we want for 'oooo').

So we only need to specify the version number as a string, and change all those integer version numbers to strings all over the code, with a few additional 'decimal' 0 digits. I.e., change current code like:

$oldversion = 2007101508; .... if ($oldversion < 2006100401) { ..... }

to

$oldversion = '2007101508.0002'; .... if ($oldversion < '2006100401.0000') { ..... }

Edit: even if we don't add the additional '0' decimal digits, the string comparison works well for us:

$oldversion = '2007101508.0002'; .... if ($oldversion < '2006100401') { ..... }

The if part only triggers if $oldversion is strictly less than '2006100401' in the 'integer' part.

So that should do it in the near term (1.9) while we devise a longer term solution.

What do you think?

Saludos. Iñaki.

In reply to Iñaki Arenaza

Re: Slightly changing how Moodle version numbers work

by sam marshall -
Picture of Core developers Picture of Peer reviewers Picture of Plugin developers
I think if you are going to change it 'all over the code' in ANY way then it should be something like the following:

if(should_do_upgrade($oldversion,2006100401)) {
// ...
}

I.e. there should be no change to widespread areas of code that doesn't turn it into a function call of some sort - so that it can be changed in future without having to change it in every area of code AGAIN, breaking compatibility for module authors.

Once you do it that way there are a variety of different options for handling versions because it will be possible to treat 'legacy' code and 'new' code in a different way.

IIRC the reason for the slightly bizarre versioning in Moodle 1.9 is strictly because they are trying to achieve this without having to alter any existing upgrade.php code.

--sam
In reply to Martin Dougiamas

Re: Slightly changing how Moodle version numbers work - easy 1.9 plan

by Martín Langhoff -

This comes in two parts, this is an easy strategy for 1.9. Not many core changes, something trivial so we can get through with the current problem without delaying or complicating things.

I will make the assumption that when you say "99 increments aren't enough" this is mainly because we are now doing a lot more version bumps to force a check for roles/caps definitions and new config entries. In other words, things that (a) are not DB changes and (b) don't really care about ordering.

What I propose is a new "version" number to track these "order-less" bumps: $CFG->revision . We can start at 1, and increment it as we want. As order doesn't matter, when we check (in index.php or admin/index.php) we check whether it has changed, not whether it has incremented.

It might be interesting to add a rule such as "use odd numbers in CVS HEAD, and even numbers in STABLE", or to skip 1000 numbers in HEAD after the _BETA branch point. Both will avoid an accidental coincidence of revision numbers that could lead to a STABLE->HEAD upgrade to "forget" to trigger the update check. I prefer the even/odd rule myself, but anything goes. The important rule is that we check for equality, not less-than.

That will free up $CFG->version to be just for updates that care about order (DB updates, mainly). And 99 has been mostly ok so far... I think wink

Edit: There might be a better name than $CFG->revision but I haven't thought much about that aspect wide eyes

In reply to Martin Dougiamas

Re: Slightly changing how Moodle version numbers work - long term plan

by Martín Langhoff -

Probably the version & revision plan above can take us quite far. But DB updates are still strictly linear, which is a bit of a pain because it restricts branched development. This in turn limits

  • How much we can do in a STABLE branch
  • How much revision we can do of code before it hits HEAD

It also forces a bit of dirty work on ddllib.php, which has to do a lot of magic to figure out if a DDL change has already been applied. Eloy's DDL magic is curiosly strong and saves us from endless problems, but there are cases that it cannot handle. The problem is fundamentally hard, and by the time ddllib gets called, there are things we no longer know, so it has to guess. It goes something like this:

Does the table/column/index I am asked to create exist already? Does it look roughly like what I'm being asked to create?

There is a lot of magic to do this reliably cross-DBs. But as you can imagine, a simple "create table and then modify it" scenario can throw this magic off, unless we attach a mind-reader extension to the code.

So here's my 2.0 plan to overcome this. It also helps people doing custom branches (which of course is what I do wink ).

First I'll describe the workflow for developers.

Workflow

In addition to db/install.xml and db/upgrade.php, each pluggable thing that has DB files (and core, naturally) have a db/upgrades/ directory.

As a developer, when you create a new DB upgrade "block", you

  • Give it a slightly longer name, following the current convention but adding some description to it, like : 2008021700-webdavlocks
  • Create a new file lib/db/upgrades/2008021700-webdavlocks.php
  • In the file, the if() that controls the block looks almost like the current one

We'll replace the if block like this

 - if ($oldversion < 2008021700) {
 + if (has_dbupdate("2008021700-webdavblocks") {

The workflow above covers development on CVS HEAD, a STABLE branch, or a custom client branch you'll never show to anyone else. For code in CVS HEAD only, there's an additional couple of steps we take when we are about to branch (the "BETA" branchpoint.

  • The release manager says there's a freeze in DB changes...
  • adds/updates a new value to version.php: $CFG->majorrelease='MOODLE_20_STABLE'
  • and then consolidates the files in lib/db/upgrades/ that are new to this major release, adding all those blocks to the end of the upgrade.php file -- and deletes the standalone files in lib/db/upgrades
  • during this conversion, the if() block gets a subtle change, as follows

    • if (has_dbupdate("2008021700-webdavblocks") {
    • if (has_dbupdate("2008021700-webdavblocks", "MOODLE_20_STABLE") {

And we are done! That's all the change.

DB upgrade blocks that were applied during the STABLE branch do not get re-attempted if they are also in the upgrade to the new major release. The upgrade to the new major release can ask if a particular upgrade from STABLE was ever applied (to undo possible damage, or to skip unneeded steps).

How it works

You probably guessed it already: we keep a simple table were we save every db upgrade that completes successfully. Call the table db_upgrades -- it has

  • id
  • pluginname (we can use 'core' or 'moodle' for lib/db
  • upgradename

So if your install is tracking STABLE or HEAD, new records get added to that table. When a new major version comes out, and the upgrades move to the upgrade.php file, the subtle change I mentioned above of doing

 - if (has_dbupdate("2008021700-webdavblocks") {
 + if (has_dbupdate("2008021700-webdavblocks", "MOODLE_20_STABLE") {

allows us to say "ah, we are on MOODLE_20_STABLE, so we have this upgrade. If it's there is a record for it in the db_upgrades table, remove it, as it's no longer needed. That way the list of upgrades we track individually stays short.

To trigger upgrades we read the lib/db/upgrades/ directories and the db_upgrades tables and trigger an update if there are files with filenames that we dont have in the db_upgrades table. With the "cleanup" strategy I outlined directory contents and the db_upgrades table will stay relatively short -- specially in production environments, not so much for developers on HEAD. We may want to keep using $CFG->version bumps for other stuff as it's generally useful, but we will have broken with the linearity of DB schema upgrades.

What do people think?

Edit: There's an alternative to the 2nd parameter in has_dbupdate() - we could cleanup the entries in the db_upgrades table if the file is gone. This has its downsides: if you upgrade to a temporary install without a specific lib/db/upgrades/XX file, and the row will be removed even though the DB schema change hasn't been undone.

In reply to Martín Langhoff

Re: Slightly changing how Moodle version numbers work - long term plan

by Petr Skoda -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers
Oh, that seems a bit too complex IMHO.

Over the time the upgrade got a bit messy, the main reason IMO is that we support skipping of major releases in upgrades - it would be much, much, much easier to write only upgrades from last major release. Most of the problems are:

* upgrade code depends on roles, but roles may not be present yet (ex: stats)
* upgrade code calls core function which depends on db table which was not upgraded yet (role rewrite in 1.9, groups upgrade, course cache, etc.)

Several times I had to go back into old upgrade scripts and rewrite the code there to make it work, unfortunately there are still some problems which sometimes force you to go through specific version to get correct upgrade.

At present 1.9 should upgrade from 1.6, 2.0 should upgrade from 1.9.0 or later only. Please note that installing 1.7.x as part of upgrade is not recommended at all.

My +1 for keeping upgrade simple+linear and supporting upgrades from last major release (or later) only in future.
In reply to Petr Skoda

Re: Slightly changing how Moodle version numbers work - long term plan

by Martín Langhoff -

Oh, that seems a bit too complex IMHO.

It's not particularly complex. For normal developers it changes where you store the upgrade code. No added steps. For MD it adds a step at branch-time, which I intend to automate with a little script.

The plan reads "long" because I described it carefully - but it is dead simple for developers, and the "machinery" behind it is 2 very straightforward functions (plus an automation script).

the upgrade got a bit messy, the main reason IMO is that we support skipping of major releases in upgrades

That is one of the main reasons, I agree. Supporting 1.6->1.9 is insanely hard.

But we cannot ignore the quite significant issue that we re-apply upgrade blocks all the time because we support upgrades on the STABLE branch as well as from major release to next major release. We have been faking things for a long while -- our development is not linear.

The first plan I posted -- with a separate $CFG->revision variable -- helps resolve the first problem you describe (with roles).

In case it's not clear... I think both plans complement each other, as they resolve different problems.

In reply to Martín Langhoff

Re: Slightly changing how Moodle version numbers work - long term plan

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
This is good. It is essentially the same as something that I have proposed informally in chats with Eloy in the past, so I am in favour.

One think you can consider is changing the upgrade procedure, so instead of udpating core first, then each module, then each block, ... etc. Instead you can first go round everywhere getting a list of all the upgrades that need to apply, then you sort them all by name - which effectively sorts them by date order, then apply them in that order. This probably reduces problems with major upgrades, e.g. 1.6->1.9, because when you are writing an update of, say, core tables, you can then be pretty confident of the state of the other tables that you might need to touch in some way.

I would even go one further and not bother with consolidating the separate upgrade files. What is the benefit of that?

Also, I do not see the need for the two-argument form has_dbupdate("2008021700-webdavblocks", "MOODLE_20_STABLE"). That second argument must be tehre to solve some problem, but I can't think what.
In reply to Tim Hunt

Re: Slightly changing how Moodle version numbers work - long term plan

by Martín Langhoff -

Cool!

go round everywhere getting a list of all the upgrades that need to apply, then you sort them all by name - which effectively sorts them by date order, then apply them in that order

That makes a ton of sense. Excellent idea - 200% with it, and it will help with the issues that Petr talks about.

not bother with consolidating the separate upgrade files. What is the benefit of that?

Parallel development, and cheap "do we have to upgrade?" checks by just calling readdir() over 20 directories and reading 1 DB table with a couple hundred rows.

When I say parallel development what I mean is that we are doing horrid stuff "merging" a supposedly linear upgrade.php . In other words, we are faking it. We are always careful, so we haven't had major trouble, and we've gotten used to it, but it imposes some awkward limitations.

The local/db thing is a workaround to these limitations, for example. When I work on long-term dev branches like mnet I often put a lot of schema changes in local/db that I later migrate to lib/db. But the whole thing is a hacked-up procedure, I end up re-labelling all the upgrade blocks several times to "fit in" and so I have to re-construct the DBs I have been working on.

I do not see the need for the two-argument form has_dbupdate("2008021700-webdavblocks", "MOODLE_20_STABLE")

I tried to explain it towards the end of the post -- have a look at the "How it works" section and the post-scriptum. It helps us cleanup the list of patches we track individually. So the list remains short, and the "should we be upgrading?" checks are always cheap, even if you install 10 moodle versions in a row.

An alternative would be to have an array that says "MOODLE_20_STABLE contains these patches" but I think making it part of the has_dbupdate() line has more semantic locality and is therefore more maintanable.

Edit: Tim - I get the feeling you replied to my post before finishing reading it, or before the coffee kicked in wink

In reply to Martin Dougiamas

Re: Slightly changing how Moodle version numbers work

by Petr Skoda -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers
I committed first upgrade with .001 and later .002, unfortunately Eloy reported problems - the upgrades kept trying to execute over and over again. Eloy tested that 2 decimal points work fine for him, so I changed the versions to .01 and 0.02. It should be IMO fine for 1.9.

Please report any problems with the floating point numbers into the tracker.

Petr