OpenOffice.org2 frustrates me like paper cuts

Possibly Ubuntu’s fault too for shipping the not-latest un-bugfixed release. But either way, it’s really annoying seeing the “Document Recovery” screen more than the edit text widget.

Copy and Paste slides in Impress is not flakey – it’s damn right crushed to pieces. Occationally it does something useful – like paste and not crash.

update: yes, i am just using it to put together presentations for our upcoming devconf – as well as the user conference. Why these things are so hard to do is beyond me. A simple app that didn’t crash *cough* magicpoint *cough* is looking rather superior at the moment.

update part 2: yes, the title changed. arguably i like this one better. although adding “right under the fingernail” is tempting

Adding an INFORMATION_SCHEMA table

I (relatively) recently added the FILES table to INFORMATION_SCHEMA (check out the patch). This was so that we could provide an SQL interface to check how much free space their was in ndb disk data files. You can see how to use this in my post entitled INFORMATION_SCHEMA.FILES (querying disk usage from SQL). We’ll have stuff hitting the manual at some point soon.

So how is an INFORMATION_SCHEMA table implemented in MySQL? Well, the first thing to realise is that INFORMATION_SCHEMA (herein refered to as I_S) tables aren’t “real” tables – they are not created by the user (in fact, the user shouldn’t – or can’t – do DDL on the I_S database). Instead, the I_S database contains tables generated on-the-fly by the MySQL Server. Internally, we create MEMORY tables and convert them to MyISAM if they get too big and then run queries on them.

So, into the code. Bring up sql_show.cc and have a look. Specifically, scroll down to ST_SCHEMA_TABLE schema_tables[] and see that it’s the same as what SHOW TABLES gets you on the I_S database.

For adding the FILES table, you can see from my patch that we just add an item to this structure. (the hunk is below)

@@ -4655,6 +4726,8 @@
fill_schema_column_privileges, 0, 0, -1, -1, 0},
{"ENGINES", engines_fields_info, create_schema_table,
fill_schema_engines, make_old_format, 0, -1, -1, 0},
+  {"FILES", files_fields_info, create_schema_table,
+   fill_schema_files, 0, 0, -1, -1, 0},
   {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table,
get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0},
{"OPEN_TABLES", open_tables_fields_info, create_schema_table,

As the comment indicates (oh my, a comment that’s correct!) you need to add an item to the enum in sql/table.h for the files table.
We’ll need to create the files_fields_info structure. This, as you guessed, describes the fields in the table. Above the schema_tables[] definition in sql_show.cc you can see the existing definitions for existing tables. Our (rather long) definition for FILES is:

ST_FIELD_INFO files_fields_info[]=
{
{"FILE_ID", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"FILE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"FILE_TYPE", 20, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLESPACE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLE_CATALOG", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"LOGFILE_GROUP_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"LOGFILE_GROUP_NUMBER", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"ENGINE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"FULLTEXT_KEYS", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0},
{"DELETED_ROWS", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"UPDATE_COUNT", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"FREE_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"EXTENT_SIZE", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"INITIAL_SIZE", 8, MYSQL_TYPE_LONGLONG, 0, 0, 0},
{"MAXIMUM_SIZE", 8, MYSQL_TYPE_LONGLONG, 0, 0, 0},
{"AUTOEXTEND_SIZE", 8, MYSQL_TYPE_LONGLONG, 0, 0, 0},
{"CREATION_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0},
{"LAST_UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0},
{"LAST_ACCESS_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0},
{"RECOVER_TIME", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"TRANSACTION_COUNTER", 4, MYSQL_TYPE_LONG, 0, 0, 0},
{"VERSION", 21 , MYSQL_TYPE_LONG, 0, 1, "Version"},
{"ROW_FORMAT", 10, MYSQL_TYPE_STRING, 0, 1, "Row_format"},
{"TABLE_ROWS", 21 , MYSQL_TYPE_LONG, 0, 1, "Rows"},
{"AVG_ROW_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Avg_row_length"},
{"DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_length"},
{"MAX_DATA_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Max_data_length"},
{"INDEX_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 1, "Index_length"},
{"DATA_FREE", 21 , MYSQL_TYPE_LONG, 0, 1, "Data_free"},
{"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"},
{"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"},
{"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"},
{"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, "Checksum"},
{"STATUS", 20, MYSQL_TYPE_STRING, 0, 0, 0},
{"EXTRA", 255, MYSQL_TYPE_STRING, 0, 0, 0},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0}
};

So now we’ve added a table to I_S and defined the table. The universal create_schema_table will take care of creating the table for us – we just need to write the function to fill the table now!

If we want to test where we’re up to now, an empty function will do. For example, you could add something like this to sql_show.cc:

int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond)
{
return 0;
}

Now you can build, run and test, but the real function is a lot more interesting. Since each storage engine can have its own files, we create a handlerton (the data structure for a table handler – see handler.h) call for the handler to fill out its bit of the FILES table.

struct run_hton_fill_schema_files_args
{
TABLE_LIST *tables;
COND *cond;
};

static my_bool run_hton_fill_schema_files(THD *thd, st_plugin_int *plugin,
void *arg)
{
struct run_hton_fill_schema_files_args *args=
(run_hton_fill_schema_files_args *) arg;
handlerton *hton= (handlerton *) plugin->plugin->info;
if(hton->fill_files_table)
hton->fill_files_table(thd, args->tables, args->cond);
return false;
}

int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond)
{
int i;
TABLE *table= tables->table;
DBUG_ENTER("fill_schema_files");

struct run_hton_fill_schema_files_args args;
args.tables= tables;
args.cond= cond;

plugin_foreach(thd, run_hton_fill_schema_files,
MYSQL_STORAGE_ENGINE_PLUGIN, &args);

DBUG_RETURN(0);
}

So, the fill_schema_files() function is what the I_S code calls. Since in MySQL 5.1 we have a much improved plugin infrastructure, we use the plugin_foreach function which iterates over the loaded storage engines in a safe way. For each storage engine, we end up calling run_hton_fill_schema_files with the args structure as a parameter. As you can see, if the handlerton has a fill_files_table function, we call it.

Now that we’re here, we need to implement fill_files_table for some table handlers. For storage engines we’re not going to implement it, we need to make sure they set it to NULL (there’s a bunch of this going on in the aforementioned patch).

Since for the NDB table handler (ha_ndbcluster.cc) we have a lovely sane init function (ndbcluster_init) it’s much easier to support new handlerton calls:

h.fill_files_table= ndbcluster_fill_files_table;

That’s all that’s needed (apart from the implementation of course :)

So, what does the implementation look like? It’s easy to see how it works from the source, so I won’t go into details here. Basically, we fill out a row using data retreived via the NDBAPI and call schema_table_store_record(). How you construct the function for your storage engine may be quite different, enjoy!

One useful query that jeb is using in some of the tests is:

SELECT FILE_NAME, (TOTAL_EXTENTS * EXTENT_SIZE) AS 'Total Extent Size',
(FREE_EXTENTS * EXTENT_SIZE) AS 'Total Free In Bytes',
(((FREE_EXTENTS * EXTENT_SIZE)*100)/(TOTAL_EXTENTS * EXTENT_SIZE))
AS '% Free Space' FROM INFORMATION_SCHEMA.FILES
+ WHERE ENGINE="ndbcluster" and FILE_TYPE = 'DATAFILE';

You may want to CREATE VIEW blah AS … for this as it could be a useful query to run regularly

NDB Disk Requirements

up to 3 copies of data (3*DataMemory)

+ 64MB * NoOfFragLogFiles (default=8)

+ UNDO log (dependent on update speed)

For example:

DataMemory=1024MB

idea on disk usage= 1024*3 + 64 * 8 =  3584MB + UNDO log

It’s very tempting to have a “SHOW ESTIMATES” command in the management client/server that performs all these equations (and the answers). I bet Professional Services would love me :)

Munich

On an internal list, a thread switched over to breifly mentioning the film Munich which incidently, I saw a few weeks ago just after linux.conf.au and really enjoyed.

I thought it was really well done and a good film. I really recommend going to see it – it’s a good cinematic experience. Possibly don’t see it if you’re feeling really sad though – not exactly a happy film. Eric Bana and Geoffrey Rush are superb in this film (both Aussies too!).

I found it to be more about his journey than anything else and enjoyed it as it was a personal story.

Oh, and why haven’t Margaret and David reviewed it yet? I would love to know what they thought. It’s not often I see a film before I’ve seen them review it :)

EnterpriseDB – WHERE IS THE SOURCE????

EnterpriseDB : Open source based relational database, PostgreSQL based database management software

Oh, that’s right – it’s a proprietary database! “Based on PostgreSQL” – well, good for them – they got a bunch of stuff for free[1]. But without a commitment to having the source out there for every release (or even the development tree) how committed are they really?

How can anybody tell if their improvements are good and well written or just utter hacks that would make you loose your lunch? (not dissing their programmers here, just pointing out that you cannot know).

Meanwhile, over here at MySQL, every single time that a developer types bk commit, an email is sent (with the content of the patch) to a public list[2]. Also, the development trees are out there, as is the source to every release in a tarball. So you can get the complete revision history of the MySQL server.[3]

That’s called committment to freedom!

[1] I understand they do pump some stuff back into PostgreSQL, but it’s still a fork with non-public bits! This also isn’t a diss on PostgreSQL.
[2] Yes, people do actually read this. I have (personally) gotten replies from people out there in the big wide world about commits I’ve made to the source tree.

[3] We’re not perfect by any means – but IMHO we’re pretty good and there’s lots of people totally committed to making sure we get better.

your work is seen by a lot of people

Oracle has 50,000 employees. Thats 50,000 people waking up each day to work on Oracle products, and those 50,000 get paid by Oracle each day. We have 50,000 people download our software everyday and work to make it better. But we dont pay them. Which model would you rather have?

A quote from our CEO Marten Mickos in “Oracle’s New Enemy” over at forbes.com.

It is pretty neat to have your work seen by that many people each day.

INFORMATION_SCHEMA.FILES (querying disk usage from SQL)

In MySQL 5.1.6 there’s a new INFORMATION_SCHEMA table.

Currently, it only has information on files for NDB but we’re hoping to change that in a future release (read: I think it would be neat).

This table is a table generated by the MySQL server listing all the different files that are/could be used by a storage engine. Three (may) be table to file mappings (or not) depending on the engine.

Basically, NDB does files like so:

A table is stored in a tablespace.

A tablespace has datafiles.

Datafiles are of a set size.

Space is allocated in datafiles to tables in a unit called an extent.

If you don’t have any free extents you cannot have new tables store data on disk.

If you don’t have any free extents you may still be able to add data to a table as there may be free space in an extent allocated to that table.

Logs (used for crash recovery) are stored in logfiles.

logfiles are part of logfile groups.

A tablespace uses a logfile group for logging.

Try the following bits of code and running SELECT * from INFORMATION_SCHEMA.FILES between each statement.

CREATE LOGFILE GROUP lg1
ADD UNDOFILE 'undofile.dat'
INITIAL_SIZE 16M
UNDO_BUFFER_SIZE = 1M
ENGINE=NDB;

ALTER LOGFILE GROUP lg1
ADD UNDOFILE 'undofile02.dat'
INITIAL_SIZE = 4M
ENGINE=NDB;

CREATE TABLESPACE ts1
ADD DATAFILE 'datafile.dat'
USE LOGFILE GROUP lg1
INITIAL_SIZE 12M
ENGINE NDB;

ALTER TABLESPACE ts1
ADD DATAFILE 'datafile02.dat'
INITIAL_SIZE = 4M
ENGINE=NDB;

CREATE TABLE t1
(pk1 INT NOT NULL PRIMARY KEY, b INT NOT NULL, c INT NOT NULL)
TABLESPACE ts1 STORAGE DISK
ENGINE=NDB;

SHOW CREATE TABLE t1;

INSERT INTO t1 VALUES (0, 0, 0);
SELECT * FROM t1;

DROP TABLE t1;
ALTER TABLESPACE ts1
DROP DATAFILE 'datafile.dat'
ENGINE = NDB;

ALTER TABLESPACE ts1
DROP DATAFILE 'datafile02.dat'
ENGINE = NDB;

DROP TABLESPACE ts1
ENGINE = NDB;

DROP LOGFILE GROUP lg1
ENGINE =NDB;

For a point of interest, these examples are taken from the ndb_dd_basic test (which can be found in mysql-test/t/ndb_dd_basic.test)

Phorum’s RSS sucks

Noticed this about our web based forums today:

the “Re: What is this? “Can’t find record in ”’ on query.”” post on the cluster forum from 10/02/06 07:53:20 isn’t the last message in that thread. there are currently 6 messages of which I only see 2.

Not only that, but from looking at the RSS, I can’t even see this post.

argh! So I shot off an email to our internal guys. The reply was that they don’t have hacking Phorum on their radar (fair enough). Of course, this just means that Phorum sucks[1] (or at least did in the version we do) and adds to the list of reasons why web based forums are much like doing $adjective to $noun.

What is it with new internet lamers and the inability to use an email program? Or even an nntp client (okay, usenet is officially crap now unless you just want spam and pornography) but the tradition of companies having internal/external nntp servers is old, tried and true.

[1] Phorum may itself be a great web based forum. It’s just that all web based forums suck – on principle.

MySQL Forums :: Cluster :: Re: Any production clusters yet ?

On the MySQL Cluster Forum, there was a thread “any production clusters yet?” to which this was a reply

I’m using NDB in production with a high-traffic web site. We have about 500,000 members and lots of surge activity at specific times. What I’m finding is that the web server goes but the cluster doesn’t break a sweat.

sweet.

comments on online documentation

Something that makes me always just go to dev.mysql.com or php.net for documentation is the user comments. sometimes you just find some real jems that are really worth reading.

It would be great if this somehow could get integrated into the (offline) help system in GNOME could somehow have this stuff integrated. maybe some AJAX foo to fetch comments from the Interweb if you’re connected. So then you get the best of three worlds: good graphical documentation interface, good documentation and user comments to the docs!

OpenOffice.org barely usable

Trying to fiddle with my linux.conf.au presentation in OpenOffice.org2 (the version that comes with Ubuntu Breezy).

It’s as buggy as all hell.

At the moment, after pasting in some slides from another presentation (if it doesn’t randomly crash some short time afterwards) I can’t save the file. Really useful.

grrr….

MySQL: Can MySQL be run from a CDROM?

MySQL: Can MySQL be run from a CDROM?

The question is asked. The answer is – yes!

Temporary tables can be store anywhere – e.g. the system’s temporary directory (/tmp on unix, probably c:\temp or something on windows… i’m sure there’s one of those %FOO% things for it).

IIRC you may need to start it with a flag or something – but the embedded library – libmysqld (where the mysql server runs within your process – you link to libmysqld instead of libmysql).

Of course, if you’re linking mysql with non-gpl code, you’ll need a license from us.

GPLv3 Draft — GPLv3

GPLv3 Draft — GPLv3

It’s there for you to take a read. I’ve just read through it and it does sound like a good improvement. I would certainly feel happy licensing my code under this license.

It’s also good to know that MySQL has been and will be further involved in the process (as are many other orgs and companies).

XML Storage Engine?

Every so often you come across people desiring intense XML and RDBMS interaction. Recently, here: Technical Notes and Articles of Interest » MySQL 5.1 is gaining some momentum.

In MySQL land, this usually means “An XML Storage Engine would be great”. hrmm… a storage engine is a way to store a table. A table is a relational thingy. Surely what’s wanted is a good way to query an XML document and search for a specific XML document.

So, is what’s really wanted good XML indexers and the ability to extract parts (or the whole) of a document? Maybe there’s some extension to fulltext (along with some funky functions) that could bring an immense amount of power with dealing with XML?

What is there now? What do people do now? What do they want to do?

All interesting stuff

White Stripes tour dates

ticketmaster.com.au – The White Stripes

Damn, damn, damn, damn damn. Only January 28th – and I’m in NZ.

Note to future organisers: make sure dates don’t overlap BDO or any really cool band tour dates.

Of course, the real disaster would be if Tool were touring at the same time as a work thing. How will people take it if i leave a company event for however long is needed to see Tool live. as many times as possible. I am dearly hoping that travel co-ordinates itself to see them in different cities, countries. Heck, even another planet if we can do that by the time the new album is ready :)

Some people don’t seem to get the Tool thing. It’s just good music. But that’s the thing – it is good music. Also, great music to hack with. I reckon each album gets played at least once per week – still.

Mangoes are good

that is all.

err… okay, a bit more. Breakfast this morning consisted of doing the dishes (good first step), some toast with jam and vegemite and a mango. Yum. All the time listening to the recording of the company-wide conf call from the other day (2am was just a little bit late that night).

These conf calls are really good actually – being able to throw questions directly at the top (and have them answered) is a great thing. Also getting to know what is going on from a higher perspective is really valuable.

Microsoft’s file system patent upheld: ZDNet Australia: News: Software

Microsoft’s file system patent upheld: ZDNet Australia: News: Software

Saying any part of the FAT file system is “novel and non-obvious” is rather like saying being stabbed in the eye with a fork is “novel and a good way to spend a sunday afternoon”.

Seriously – what the?

I’m really glad I work for a company that opposes software patents.

Thanks to Pia for the links.

Bug 15695 and NDB initial start

The process for starting up a cluster is pretty interesting. Where, of course, “interesting” is translated to “complex”. There’s a lot of things you have to watch out for (namely you want one cluster, not two or ten or anything). You also want to actually start a cluster, not just wait forever for everybody to show up.

Except in some situations. For example, initial start. With an initial start, you really want to have all the nodes present (you don’t want to run the risk of starting up two separate clusters!).

Bug 15695 is a bug to do with Initial Start. If you have three nodes (a management node and two data nodes) and break the network connection just between the two data nodes, and then reconnect it (at the wrong time – where the wrong time means you trigger the bug) the cluster will never start. A workaround is to restart one of the data nodes and everything comes up.

Note that this is just during initial start so it’s not a HA bug or anything. Just really annoying.

This seems to get hit when people have firewalls stopping the nodes talking to each other and then fix the firewall (but not shutting down the cluster).

As is documented in the bug, you can replicate this with some iptables foo.

One of the main blocks involed in starting the cluster (and managing it once it’s up) is the Quorum manager – QMGR. You’ll find the code in ndb/src/kernel/blocks/qmgr/. You’ll also find some in the older CMVMI (Cluster Manager Virtual Machine Interface).

A useful thing to do is to define DEBUG_QMGR_START in your build. This gives you some debugging output printed to the ndb_X_out.log file.

The first bit of code in QmgrMain.cpp is the heartbeat code. execCM_HEARTBEAT simply resets the number of outstanding heartbeats for the node that sent the heartbeat. Really simple signal there.

During SR (System Restart) there is a timeout period for which we try to wait for nodes to start. This means we’ll be starting the cluster with the most number of nodes present (it’s much easier doing a SR with as many nodes as possible than doing NR – Node Recovery – on lots of nodes). NR requires copying of data over the wire. SR probably doesn’t. Jonas is working on optimised node recovery which is going to be really needed for disk data. This will only copy the changed/updated data over the wire instead of all data that that node needs. Pretty neat stuff.

We implement the timeout by sending a delayed signal to ourself. Every 3 seconds we check how the election of a president is going. If we reach our limit (30seconds) we try to start the cluster anyway – not allowing other nodes to join).

Current problem is that each node in this two node not-quite-yet cluster thinks it has won the election and so switches what state it’s in to ZRUNNING (see Qmgr.h) hence stopping the search for other nodes. When the link between the two nodes is brought back up – hugs and puppies do not ensue.

I should have a patch soon too.

For a more complete explanation on the stages of startup, have a look at the text files in ndb/src/kernel/blocks. Start.txt is a good one to read.

new .emacs snippet

for the non lisp hackers – this sets some c mode options depending on the name of the path to the source file.


;; run this for mysql source
(defun mysql-c-mode-common-hook () (setq indent-tabs-mode nil))

;; linux kernel style
(defun linux-c-mode-common-hook () linux-c-mode)

(setq my-c-mode-common-hook '(lambda ()
(turn-on-font-lock)
(setq comment-column 48)
)
)

;; predicates to check
(defvar my-style-selective-mode-hook nil)

(add-hook 'my-style-selective-mode-hook
'((string-match "MySQL" (buffer-file-name)) . mysql-c-mode-common-hook)
)

(add-hook 'my-style-selective-mode-hook
'((string-match "linux" (buffer-file-name)) . linux-c-mode-common-hook)
)

;; default hook
(add-hook 'my-style-selective-mode-hook
'(t . my-c-mode-common-hook) t)

;; find which hook to run depending on predicate
(defun my-style-selective-mode-hook-function ()
"Run each PREDICATE in `my-style-selective-mode-hook' to see if the
HOOK in the pair should be executed. If the PREDICATE evaluate to non
nil HOOK is executed and the rest of the hooks are ignored."
(let ((h my-style-selective-mode-hook))
(while (not (eval (caar h)))
(setq h (cdr h)))
(funcall (cdar h))))

;; Add the selective hook to the c-mode-common-hook
(add-hook 'c-mode-common-hook 'my-style-selective-mode-hook-function)