List:Commits« Previous MessageNext Message »
From:Rafal Somla Date:December 19 2008 4:20pm
Subject:Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)
WL#4630
View as plain text  
Hi Ingo,

Now, when I'm reviewing your backup client code, I found time to read this email 
thoroughly. See some of my comments below. Sorry for such a late reply.

Ingo Strüwing wrote:
> Hi Øystein,
> 
> thank you very much for the first part of a very thorough review.
> Please find answers below.
> 
> Øystein Grøvlen, 11.12.2008 14:24:
>> Ingo,
>>
>> Thanks, for this thorough and well tested patch.  I have not been
>> through all of it, yet, but here are the comments and questions I have
>> so far:
>>
...
>>  * Some questions from playing a bit with the client:
>>
>>      - What does "extra data length" mean?  And why is it always 0?
> 
> It is the length of the "extra data" that could be present in the backup
> image for an object. Currently, we do not write extra data to the backup
> image. Hence it is always 0.
>

Perhaps no information about extra data should be printed if it is not present...


>>      - Finish time is always 0.  Why?  It is set correctly in the
>>        backup_history table for the same backup.
> 
> I do not know. It is the value that the stream library provides. I guess
> it is not written to the backup file, or the stream library does not
> fill this element of the "header" when reading the image.
> 

This is a bug in backup kernel which does not write that information to the 
backup image (although it reports it in backup logs). I think it is already 
fixed with one of my patches but not sure if the patch has been pushed already.

...
>>  * Took me some time to understand the role of the bcat_ functions.  I
>>    think this should be explained a bit better.  Do the implementation
>>    have to be this separate for the client and the server, or would it
>>    be possible to share some more code?
> 
> It took me some time too. To say the least. These functions are defined
> by the stream library interface. I had no influence on their architecture.
> 

Does that mean that the documentation provided in stream_v1_service.h is not 
clear enough? Any suggestions how to improve it?

> I must admit that I didn't spend much time in analyzing how to share
> code with the server. Some small experiments required me to include most
> of the server code, which didn't look right. I would also need to learn,
> how the catalog is constructed in the server. A quick look shows that a
> couple of classes are involved. I usually have a hard time to follow all
> the classes used in the backup code. Since not even Rafal suggested to
> do it, I didn't even try.

No, the backup code is not designed to be used by others. If we want to reuse 
it, some more design thinking and refactoring would be required (define public 
interfaces!)

>>  * I would prefer if the addition of error injection to mysys routines
>>    was done as a separate patch.  This is a general mechanism and I
>>    feel that is not related to backup.  I also think other people than
>>    members of the backup team should review this.
> 
> I can't imagine, how this could be done. Such a task wouldn't get
> sufficient priority to be looked at by anyone.
> 
> But I could remove it from the backup client patch. I have tested, what
> I wanted to test with it.
>

I opt for leaving it there.

...
>> Some more detailed questions/comments inline:
>>
>> ...
>>
>>> === modified file '.bzrignore'
>>> --- a/.bzrignore    2008-10-29 14:20:12 +0000
>>> +++ b/.bzrignore    2008-11-25 19:33:34 +0000
>>> @@ -314,6 +314,10 @@ client/mysql_upgrade
>>>  client/mysqladmin
>>>  client/mysqladmin.c
>>>  client/mysqladmin.cpp
>>> +client/mysqlbackup
>>> +client/stream_v1.h
>>> +client/stream_v1_services.h
>>> +mysql-test/mysqlbackup-test.bak
>> Why are the three last entries added?
> 
> I want bzr to ignore these files when running gcommit.
> 
> client/stream_*.h are symbolic links to files in sql/backup. They are
> required for #include directives. The Makefile for the client directory
> does not use an -I../sql option, so I didn't add an -I../sql/backup
> option either.
> 

I really think that backup stream library should be used as a library. However, 
it means it needs to be compiled early in the build process and public headers 
need to be made available in some common directory. All this requires changes 
outside the sql/backup/ i.e., outside our domain. This is why it was not done 
yet and I think it requires a separate task. Until then, I'd consider Ingo's 
solution temporary - ultimately we should not link the library sources or 
headers here.

...
>>> +/**
>>> +  Read from the stream/image.
>>> +
>>> +  @param[in,out]    strm        stream handle, updating position
>>> +  @param[in,out]    data        data container, updating contents and
>> ptrs
>>
>> I do not think it is easy to understand from this comment what is
>> producer and comsumer here.  And what "updating contents and ptrs"
>> implies.
> 
> Agree. I tried to keep the parameter descriptions short. This didn't
> work out well.
> 
> Please note that this is also a callback for the stream library. It's
> signature is specified in stream_v1_services.h:
> 
> +  /**
> +    Function reading bytes from the underlying media.
> +    For specification, see @c bstream_read_part() function.
> +  */
> +  typedef int (*as_read_m)(void *, bstream_blob*, bstream_blob);
> 
> However, bstream_read_part() is the caller of the as_read_m typed
> function. So this comment basically says "Go and figure out yourself,
> how it is used." That's what I had to do.
> 

The intention was to say that as_read_m, which has the same parameters as 
bstream_read_part() should also implement the same behaviour (both functions are 
reading bytes from the underlying stream).

I don't like copying the same documentation in two places because then they are 
bond to diverge eventually.

Would such change in the documentation explain it better?

=== modified file 'sql/backup/stream_v1_services.h'
--- sql/backup/stream_v1_services.h     2007-11-29 19:58:12 +0000
+++ sql/backup/stream_v1_services.h     2008-12-19 13:06:09 +0000
@@ -187,16 +187,27 @@ int bcat_create_item(struct st_bstream_i
  /**
    Function writing bytes to the underlying media.

-  For specification, see @c bstream_write_part() function.
+  Writes bytes stored in the specified fragment of the buffer, the same as is
+  done in @c bstream_write_part() function. See documentation of that function
+  for details.
+
+  @retval BSTREAM_OK at least one byte was written (or copied to the output buffer)
+  @retval BSTREAM_ERROR  error was detected
  */
-typedef int (*as_write_m)(void*, bstream_blob*, bstream_blob);
+typedef int (*as_write_m)(void *s, bstream_blob *data, bstream_blob buf);

  /**
    Function reading bytes from the underlying media.

-  For specification, see @c bstream_read_part() function.
+  The function stores bytes in the specified fragment of the buffer, the same
+  as is done in @c bstream_read_part() function. See documentation of that
+  function for details.
+
+  @retval BSTREAM_EOS  end of stream was detected while reading data
+  @retval BSTREAM_ERROR error was detected while reading data
+  @retval BSTREAM_OK   successful read - at least one byte was read
  */
-typedef int (*as_read_m)(void *, bstream_blob*, bstream_blob);
+typedef int (*as_read_m)(void *s, bstream_blob *data, bstream_blob buf);


If yes, could you include it in your patch? If not, what remains unclear?

> Fortunately, at least the function comment of bstream_read_part() tries
> to explain, how "bstream_blob"s are used.
> 

And succeeds? If not, then what is the problem?

> And, there is a reference implementation in stream.cc: stream_read().
> Unfortunately, its comments doesn't add much insight.
> 
> In the end I was happy that I made it work at all. I didn't feel much of
> an obligation to explain it better than what I had to work with.
> 
> But now I see, I was in error. I take the blame. How about this description:
> 
> The 'data' parameter is a reference to an st_blob structure, which
> contains two pointers, 'begin' and 'end'. When the function is called,
> the pointers point into a buffer to receive the data from the stream.
> They are to be placed starting at 'begin' and added up to 'end'. On
> return of the function, the 'end' pointer is adjusted towards 'begin',
> if less than the requested amount of data was read from the stream.

I think it is 'begin' pointer which is adjusted towards 'end':

   buf  [---------------=================--------]

   data blob before reading:

                       [=================]

   data blob after reading:

                        **********[======]
                        ^          ^
                        |          space remaining free
                        bytes which have been read


> 
>>> +  @param            envelope    not used
>> What is the intention of envelope?
> 
> From the reference implementation and the calling function, I guess it
> is the buffer as it has been allocated and into which the 'data'
> pointers point. It is not necessary that 'data->begin' is at
> 'envelope->begin', nor that 'data->end' is at 'envelope->end'. But both
> 'data' pointers must be between the 'envelope' pointers.
> 
> However, the parameter is never used. Not in my implementation, nor in
> the reference implementation. I don't even have an idea, what it could
> be useful for.
> 

If I remember correctly the intention was to not exclude a following, very 
remote I agree, use scenario:

Suppose that the underlying stream is organized so that the proper content is 
wrapped in some kind of headers/footers. For example checksums or something like 
this. Having such interface to the stream read function allows the 
implementation to use the provided buffer to store the header/footer data if 
there is enough space for it. So the implementation could work like this:

If next thing in the stream is N-bytes long header followed by data then do the 
following:
  1. See if there is N-bytes available between beginning of the buffer and
     start of the area where data should be stored.
  2. If yes, then save these N-bytes on a side.
  3. Read into the buffer (the envelope) the header followed with as much data as
     needed (and can fit) aligned so that data falls in the correct place.
  4. Do header processing.
  5. Restore the N-bytes overwriting the header.

This way implementation can use the provided buffer for low-level reads and 
avoid copying big amounts of data (as compared to the presumably short 
header/footer).

I'm not going to defend this design or argue that we need this level of 
generality. I designed it like this because I like general designs. Later it was 
not refused by reviewers and... here it is!


>>> +static int
>>> +str_open_rd(struct st_stream *strm, const char *path, uint *version_p)
>>> +{
>>> +  MY_STAT               stat_area;
>>> +  size_t                lgt;
>>> +  int                   rc= BSTREAM_OK;
>>> +  int                   errpos= 0;
>>> +  uint16                version;
>>> +  const unsigned char   backup_magic_bytes[8]=
>>> +    {
>>> +      0xE0, // ###.....
>>> +      0xF8, // #####...
>>> +      0x7F, // .#######
>>> +      0x7E, // .######.
>>> +      0x7E, // .######.
>>> +      0x5F, // .#.#####
>>> +      0x0F, // ....####
>>> +      0x03  // ......##
>>> +    };
>> I suggest that these magic bytes are defined a place where the same
>> definition could be used for both server and client.
> 
> Hehe. I tried this with another define. The problem is that the headers
> in sql/backup/ are heavily interwoven with server code.
> 
> The two exceptions stream_v1.h and stream_v1_services.h are not
> appropriate either, because Rafal's architecture is so that the magic
> bytes will never change, while a new version of the stream library may
> change everything. So we would probably need a new header file. I'm
> awaiting your proposal.
> 

Yes the current backup stream library is not a good place to put it there 
because it is not intended to read any type of backup stream, but only a backup 
image in format v1. In fact, contrary to its name which is perhaps ill chosen, 
the library is encapsulating not a backup stream as such but rather the format 
of the backup image, by providing stream interface for reading/writing an image 
in that format.

In the future, there will be more formats around, and more libraries each 
describing a single format (at least that was the idea). Then it would be 
perhaps good to have a top level library, which will be able to read stream, 
detect the format and use correct format-specific library to interpret the rest 
of the stream. Such top-level library would be a proper place to export the 
backup magic bytes.

Note however that these magic bytes are meant to be "written in stone" as for 
example the magic bytes for gzip compressed data.

>>> +/**
>>> +  Create global iterator of a given type.
>>> +
>>> +  Possible iterator types.
>>> +
>>> +  - BSTREAM_IT_CHARSET:    all charsets
>>> +  - BSTREAM_IT_USER:       all users
>>> +  - BSTREAM_IT_TABLESPACE: all tablespaces
>>> +  - BSTREAM_IT_DB:         all databases
>>> +
>>> +  The following types of iterators iterate only over items for which
>>> +  some meta-data should be saved in the image.
>>> +
>>> +  - BSTREAM_IT_GLOBAL:     all global items in create-dependency order
>>> +  - BSTREAM_IT_PERDB:      all per-db items except tables which are
>> enumerated
>>> +                           by a table iterator (see below)
>>> +  - BSTREAM_IT_PERTABLE:   all per-table items in create-dependency
>> orders.
>>> +
>> I do not quite understand this comment.  Are the three latter no
>> possible iterator types since they are in another list?
> 
> I'm not sure either. I copied the comment verbatim from the stream
> library interface definition in stream_v1_services.h.
> 
> I guess that "Possible iterator types" applies to all the iterator
> types, but that the latter three have an additional comment.
>

Yes, that was the intention.

...
>>> +
>>> +  case BSTREAM_IT_DB:
>>> +    iter->it_array= &bup_catalog->cat_databases;
>>> +    DBUG_PRINT("bupstrm", ("database"));
>>> +    break;
>>> +
>>> +    /* purecov: begin inspected */
>>> +  case BSTREAM_IT_TABLESPACE:
>>> +    iter->it_array= &bup_catalog->cat_tablespaces;
>>> +    DBUG_PRINT("bupstrm", ("tablespace"));
>>> +    goto err;
>>> +
>>> +  case BSTREAM_IT_GLOBAL:
>>> +    errm("iterator type not yet implemented: global\n");
>>> +    DBUG_PRINT("bupstrm", ("NOTYET implemented: global"));
>>> +    goto err;
>>> +
>>> +  case BSTREAM_IT_PERDB:
>>> +    errm("iterator type not yet implemented: perdb\n");
>>> +    DBUG_PRINT("bupstrm", ("NOTYET implemented: perdb"));
>>> +    goto err;
>>> +    /* purecov: end */
>> Ditto.
> 
> Well, I don't know why these have not been used during all the tests I
> did. There were definitely tablespaces and views (per-db items) in the
> backup files.
> 
> A vague guess may be that all of these have no sub-objects. So that the
> stream library does not need to access the "parent" structure ever. Only
>  the databases iterator was used. The catalogs contained sub-items of
> databases. When handing over such sub-type to the bcat_add_item()
> function, it sets up the item structure with a reference to the database
> object within the application's catalog. To get at that reference, it
> needs to use an iterator to find the database by its "catalog position".
> 

Yes, it is a good guess. Other kinds of iterators are used only when performing 
restore.

...
>>> +enum enum_bstream_ret_codes
>>> +backup_read_catalog(void* image_handle, struct st_backup_catalog
>> *bup_catalog)
>>> +{
>>> +  struct st_stream *strm= (struct st_stream*) image_handle;
>>> +  enum enum_bstream_ret_codes brc;
>>> +  DBUG_ENTER("backup_read_catalog");
>>> +  DBUG_ASSERT(strm);
>>> +  DBUG_ASSERT(bup_catalog);
>> Would it not be appropriate here and in the other similar _read_
>> functions to assert that stream_pos is as expected?
> 
> - This would not work with the current way, I assign string literals to
> stream_pos (unless we do string comparison). I would at least have to
> use file-global string constants, if not enums as you suggested. Since I
> found that I did a string compare for "meta data", I did now define
> string constants like: const char *STREAM_POS_META_DATA= "meta data". So
> this argument is void now.
> 
> - Assertions are used to catch flaws in the implementation. Wrong stream
> pos is more likely to happen due to data corruption. Then it is better
> to check with 'if'.
> 
> - I guess that this check is done in the stream library. But if you
> think we should check it here anyway, then ok.
> 

No, it is not. But perhaps it should! I did not get that idea when writing the code.

> - It may take some time to figure out allowed combinations. For example,
> I guess that table data do not need to be present. If the summary
> follows the meta data, this should proably be accepted. Summary can be
> inline in the header. Etc.
> 

Yep, there are few combinations. With help from Paul, I hope we will have backup 
image format well documented soon.

...
>>> +/*
>>> +  Catalog items are located by "catalog coordinates". The format of the
>>> +  catalog coordinates depends on the type of item. It is specified as
>>> +  follows:
>>> +
>>> +  [item position (global)]= [db no.]
>>> +  [item position (table)]= [ snap no. ! pos in snapshot's table list ]
>>> +  [item position (other per-db item)]= [ pos in db item list ! db no. ]
>>> +  [item position (per-table item)] = [ pos in table's item list !
>>> +                                       db no. ! table pos ]
>> What does '!' mean.  If it is just a separator, please, choose
>> another character so that people do not believe it has something to do
>> with negation.
> 
> I copied the from stream_v1.c. I hesitate to introduce an inconsistent
> syntax notation here.
> 

Paul is working now on this document: 
http://forge.mysql.com/wiki/MySQL_Internals_MySQL_Backup_Stream_API
Perhaps you could use notation from there. Unfortunately, item coordinates are 
not covered yet (I just sent an email about this).

Rafal
Thread
bzr commit into mysql-6.0-backup branch (ingo.struewing:2745) WL#4630Ingo Struewing25 Nov
  • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Øystein Grøvlen11 Dec
    • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Ingo Strüwing12 Dec
      • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Rafal Somla19 Dec
        • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Ingo Strüwing10 Feb 2009
          • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Rafal Somla10 Feb 2009
  • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Rafal Somla22 Dec
    • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Ingo Strüwing12 Feb 2009
      • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Rafal Somla17 Feb 2009
        • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Ingo Strüwing17 Feb 2009
  • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Øystein Grøvlen17 Feb 2009
    • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Ingo Strüwing18 Feb 2009
      • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Øystein Grøvlen19 Feb 2009
        • Re: bzr commit into mysql-6.0-backup branch (ingo.struewing:2745)WL#4630Ingo Strüwing21 Feb 2009