You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Daniel Shahaf <d....@daniel.shahaf.name> on 2008/10/03 06:39:09 UTC

Re: svn commits: r33357, r33358

Took me a few days to get to these...

r33357:
> Continued evoluation of the metadata schema.
> 
> * subversion/libsvn_wc/wc-metadata.sql3:
>   (NODE, NODE_CHANGES):
>     - move the actual_size field to this table, from the TEXT_BASE table.
>       two different nodes may share the same text base, but apply different
>       EOL and/or keywords translations to it, thus having different sizes
>       within the WORKING/ACTUAL trees.
>     - place the checksum directly into this table, rather than a "text_id"
>       field. due to operational issues, the text_id may not be a satisfiable
>       foreign key (we may not want to insert a row). 

Can you explain this scenario?  When do you see TEXT_ID not a valid
foreign key *and* CHECKSUM a valid foreign key (at the same time)?
Before this change, the schema was "id PRIMARY KEY, checksum NOT NULL".

>       thus, the ideal foreign
>       key is "checksum" which *may* exist in the TEXT_BASE table.
>   (TEXT_BASE):
>     - renamed from BASE_TEXT. we use "text base" more often.
>     - drop the "id" field, and key off of checksum instead.
>     - compression is now optional (rather than planning on a particular
>       value to mean "no compression").
>     - stored_size is now optional to detect when the pristine has been
>       properly stored (or not).
> 

> -CREATE TABLE BASE_TEXT (
> -  id  INTEGER PRIMARY KEY AUTOINCREMENT,
> -
> +CREATE TABLE TEXT_BASE (
>    /* ### the hash algorithm (MD5 or SHA-1) is encoded in this value */
> -  checksum  TEXT NOT NULL,
> -
> -  /* ### enumerated values specifying type of compression */
> -  compression  INTEGER NOT NULL,
> +  checksum  TEXT NOT NULL PRIMARY KEY,
>  

Okay.  A certain text-base may appear more than once (if we change the
hash algorithm used), but this won't break anything.  (The row may be
shared, so we'll have to create a new one rather than just modify the
CHECKSUM column's value.)

> +  /* ### used to verify the pristine file is "proper". NULL if unknown,
> +     ### and (thus) the pristine copy is incomplete/unusable. */
> +  stored_size  INTEGER,

Do we need this?

Most of the time the pristine files don't change under our feet.  (When
they do, they're corrupted.)  So we can skip the size check and always
do only the more expensive check (calculating the checksum).  And if we
skip the size check, we won't need stored_size in the database.

?

> -  refcount INTEGER NOT NULL
> +  refcount  INTEGER NOT NULL

This value can be derived from the other tables, can't it?  (See?  Who
said whitespace-only changes were useless?)

>    );


r33358:
> --- trunk/subversion/libsvn_wc/wc_db.h	2008/09/30 04:39:20	33357
> +++ trunk/subversion/libsvn_wc/wc_db.h	2008/09/30 04:58:37	33358
> +/* ### @a new_refcount may be NULL */
> +svn_error_t *
> +svn_wc__db_pristine_decref(int *new_refcount,
> +                           svn_wc__db_pdh_t *pdh,
> +                           svn_checksum_t *checksum,

So, sometimes 'svn_checksum_t *checksum' is the primary key of the
operation, and sometimes it just describes a file passed in another arg.
Personally I'd find it easier to read if it were named differently
depending on its role -- e.g., here it might be named 'svn_checksum_t
*text_base_id'.  That conveys more information than calling it 'checksum'.

> /*
>  * wc_db.c :  manipulating the administrative database
..
> static svn_error_t *
> get_pristine_fname(const char **path,
>                    svn_wc__db_pdh_t *pdh,
>                    svn_checksum_t *checksum,
>                    svn_boolean_t create_subdir,
>                    apr_pool_t *result_pool,
>                    apr_pool_t *scratch_pool)
> {
>   const char *hexdigest = svn_checksum_to_cstring(checksum, scratch_pool);
>   char subdir[3] = { 0 };
> 
>   /* We should have a valid checksum and (thus) a valid digest. */
>   SVN_ERR_ASSERT(hexdigest != NULL);
> 
>   /* Get the first two characters of the digest, for the subdir. */
>   subdir[0] = hexdigest[0];
>   subdir[1] = hexdigest[1];
> 

*nod*  Works for any non-zero length of hexdigest[].  Nice :)

> svn_error_t *
> svn_wc__db_open_many(svn_wc__db_t **db,
>                      const apr_array_header_t *paths,
>                      svn_config_t *config,
>                      apr_pool_t *result_pool,
>                      apr_pool_t *scratch_pool)
> {
...
>   for (i = 0; i < paths->nelts; ++i)
>     {
>       const char *path = APR_ARRAY_IDX(paths, i, const char *);
>       svn_node_kind_t kind;
>       svn_boolean_t special;
>       svn_wc__db_pdh_t *pdh;
> 
>       /* If the file is special, then we need to refer to the encapsulating

Contrary to the comment, the boolean variable 'special' is not used in
the function.

>          directory instead, rather than resolving through a symlink to a
>          file or directory. */
>       SVN_ERR(svn_io_check_special_path(path, &kind, &special, scratch_pool));
> 
...
> }
> 
> 
> svn_error_t *
> svn_wc__db_pristine_dirhandle(svn_wc__db_pdh_t **pdh,
>                               svn_wc__db_t *db,
>                               const char *dirpath,
>                               apr_pool_t *result_pool,
>                               apr_pool_t *scratch_pool)
> {
>   /* ### need to fix this up. we'll probably get called with a subdirectory
>      ### of some dirpath that we opened originally. that means we probably
>      ### won't have the subdir in the hash table. need to be able to
>      ### incrementally grow the hash of per-dir structures. */
> 
>   *pdh = apr_hash_get(db->dir_data, dirpath, APR_HASH_KEY_STRING);
> 
>   return SVN_NO_ERROR;
> }

The metadata location (per-dir, wc root, etc.) is typically per-wc, not
per-versioned-dir.  As the code is, we'll just set a key in the hash for
every single directory in the wc, recursively.  But we could store the
information (whether the pristine dir is stored in-place or at the wc
root) just for the wc root and let deeper dirs inherit its setting.

Also, for the values, why use strings?  An enum should cover the 2-3
most common cases ("in ./.svn", "in wc root" (we know where it is),
"in ~/.subversion").

> 
> 
> svn_error_t *
> svn_wc__db_pristine_decref(int *new_refcount,
>                            svn_wc__db_pdh_t *pdh,
>                            svn_checksum_t *checksum,
>                            apr_pool_t *scratch_pool)
> {
>   return svn_error__malfunction(__FILE__, __LINE__, "Not implemented.");
> }

Interesting spelling of the error handling.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Listman <li...@burble.net>.
On Oct 5, 2008, at 11:30 PM- Oct 5, 2008, Greg Stein wrote:

> On Sun, Oct 5, 2008 at 2:57 PM, Listman <li...@burble.net> wrote:
>> ...
>> scanning the filesystem is not an option for larger repositories, svn
>> update, status etc
>> already take 20 mins for some of my repositories, i'm really hoping  
>> that we
>> can resolve this
>> with wc-ng
>
> Unless I've totally forgotten something, "svn update" won't scan *at
> all*. It'll read the SQLite database, see what versions you have, and
> send that to the server. There is no need for ... oh darn. Just as I
> say that, I remember missing directories/files. We need to look for
> stuff that's missing, which is thankfully a more limited task than the
> current scanning.
>
> For svn status, I want to have a mode that is "give me something
> close". It will just show you the state of the WORKING tree, and not
> hit the filesystem beyond the SQLite database. It'll show you all the
> adds, copies, deletes, etc. If you use "svn edit", then it will also
> show administratively-edited files. And... if you do a normal "svn
> status", then it can update the database with seen-edits so the next
> time you do the "quick" mode, it will also show those (tho they may be
> stale if you edit a file back to pristine).
>

great, this is will help us buckets..

> svnversion will definitely avoid scanning. That is just reading the
> versions out of the SQLite database. I did that test code in the
> branch, and it all looks good.
>

great

> Checkouts for large repositories could be fast if you have multiple
> WCs from the same repos, *and* you use a central datastore. In that
> case, it will skip asking the server for content, and just fetch it
> from its system-wide set of pristine files.
>

we have up to 50 user working copies per repos typically..


> So... yah. There should be a ton of improvements in day-to-day work.
>
> Cheers,
> -g
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: dev-help@subversion.tigris.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Greg Stein <gs...@gmail.com>.
On Sun, Oct 5, 2008 at 2:57 PM, Listman <li...@burble.net> wrote:
>...
> scanning the filesystem is not an option for larger repositories, svn
> update, status etc
> already take 20 mins for some of my repositories, i'm really hoping that we
> can resolve this
> with wc-ng

Unless I've totally forgotten something, "svn update" won't scan *at
all*. It'll read the SQLite database, see what versions you have, and
send that to the server. There is no need for ... oh darn. Just as I
say that, I remember missing directories/files. We need to look for
stuff that's missing, which is thankfully a more limited task than the
current scanning.

For svn status, I want to have a mode that is "give me something
close". It will just show you the state of the WORKING tree, and not
hit the filesystem beyond the SQLite database. It'll show you all the
adds, copies, deletes, etc. If you use "svn edit", then it will also
show administratively-edited files. And... if you do a normal "svn
status", then it can update the database with seen-edits so the next
time you do the "quick" mode, it will also show those (tho they may be
stale if you edit a file back to pristine).

svnversion will definitely avoid scanning. That is just reading the
versions out of the SQLite database. I did that test code in the
branch, and it all looks good.

Checkouts for large repositories could be fast if you have multiple
WCs from the same repos, *and* you use a central datastore. In that
case, it will skip asking the server for content, and just fetch it
from its system-wide set of pristine files.

So... yah. There should be a ton of improvements in day-to-day work.

Cheers,
-g

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Daniel Shahaf <d....@daniel.shahaf.name>.
> > If .svn is absent, we can ignore the error and fall back to scanning the
> > filesystem.  If all it stores is the wc root location, we won't have lost
> > anything.
> 
> scanning the filesystem is not an option for larger repositories, svn
> update, status etc already take 20 mins for some of my repositories,
> i'm really hoping that we can resolve this with wc-ng
> 

These operations scan downwards (scan all of a dir's children).  Finding 
the wc root (even in current libsvn_wc) requires scanning *upwards* -- 
scanning all of a dir's parents.  The cost is proportional to the number 
of path components you use.

Daniel


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Listman <li...@burble.net>.
On Oct 5, 2008, at 12:00 PM- Oct 5, 2008, Daniel Shahaf wrote:

> Greg Stein wrote on Sun, 5 Oct 2008 at 11:27 -0700:
>> On Sun, Oct 5, 2008 at 9:30 AM, Daniel Shahaf  
>> <d....@daniel.shahaf.name> wrote:
>>> ...
>>>> Well, checking the size *does* cost a stat(). If you look at the  
>>>> mode
>>>> to svn_wc__db_pristine_check(), you'll see different ways to  
>>>> check for
>>>> the presence of a pristine file. One of the modes won't even  
>>>> touch the
>>>> file -- it just relies on what is in SQLite. The "single" and  
>>>> "multi"
>>>> modes are good for "svn update" where you want to ask "hey. do I  
>>>> have
>>>> this file?" ... you can get a "no" answer really fast.
>>>
>>> Actually, I assumed that since we read the file (from disk)  
>>> already, the
>>> cost of the stat() of the same file would be negligible.
>>
>> Eh? No... we haven't necessarily read in the pristine file. We're
>> talking about the case where we're *considering* the pristine file.  
>> Is
>> it present? Is it usable? Should we fetch one during an "svn update"?
>> etc. In these cases, we might stat() the file to compare its size
>> against what is in the sqlite row, to verify the file is (still)  
>> there
>> and its size is proper.
>>
>
> *When* we have to validate the pristine file (bolded because it's
> a separate discussion), we can either open the database or open or  
> stat
> the file.  The checkmode_t constants probably allow the flexibility
> needed here (and if not, we'll discover it when we using them).
>
> (In a side comment, it feels weird that they are named after "what
> caller wants to do" and documented in terms of "what the  
> implementation
> really does".)
>
>>> ...
>>>>>> During a single svn command, you might hit some file in a  
>>>>>> subdirectory
>>>>>> as one of the arguments. The fact that it is "under" one of the
>>>>>> previously-discovered wcroots is NOT sufficient. You still have  
>>>>>> to
>>>>>> scan upwards to find a switched root, or an svn:external or  
>>>>>> somesuch.
>>>>>
>>>>> If you have .svn dirs: store the URL given to 'svn checkout' (or  
>>>>> the key
>>>>> to the WORKING_COPY table).
>>>>
>>>> Assume we don't. There will only be one .svn directory, at the  
>>>> wcroot.
>>>
>>> I assumed the in-tree .svn dirs would just point to the wc root
>>> (basically 'ln -s ../../../../   .svn').  Are they really that bad?
>>> They would save all these upwards-scans-in-the-fs all the time.
>>
>> Yes, they are. People have asked for years to get rid of them.
>> Primarily, it seems to come from the Mac dev community -- they have
>> some tools that assume they know everything in a given directory, and
>> when we throw something in there... they go wonky. Or they rebuild  
>> the
>> directory and leave our .svn behind. Crap like that.
>>
>
> If .svn is absent, we can ignore the error and fall back to scanning  
> the
> filesystem.  If all it stores is the wc root location, we won't have  
> lost
> anything.

scanning the filesystem is not an option for larger repositories, svn  
update, status etc
already take 20 mins for some of my repositories, i'm really hoping  
that we can resolve this
with wc-ng

i also have many tools that delete the .svn directories on a regular  
basis, with
the result that i have to spend time fixing up user working copies on  
a regular basis.

Please, please lets move the .svn dir's out of the working copy  
directories. Its just too
fragile to leave it there..


>
>
>> The .svn subdirectories also play hell with various "find"-based
>> commands. e.g. searching for something, counting lines or files, or
>> whatever.
>>
>
> Again, I'm not convinced that this really applies if it's just a  
> pointer
> to the wc root.  A 'grep' will only break for the wc root (and even  
> then,
> there was the suggestion to store .svn in the wc root's parent), and
> counting lines will add you.. what?  One line per directory?




---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Daniel Shahaf <d....@daniel.shahaf.name>.
Greg Stein wrote on Sun, 5 Oct 2008 at 11:27 -0700:
> On Sun, Oct 5, 2008 at 9:30 AM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
> >...
> >> Well, checking the size *does* cost a stat(). If you look at the mode
> >> to svn_wc__db_pristine_check(), you'll see different ways to check for
> >> the presence of a pristine file. One of the modes won't even touch the
> >> file -- it just relies on what is in SQLite. The "single" and "multi"
> >> modes are good for "svn update" where you want to ask "hey. do I have
> >> this file?" ... you can get a "no" answer really fast.
> >
> > Actually, I assumed that since we read the file (from disk) already, the
> > cost of the stat() of the same file would be negligible.
> 
> Eh? No... we haven't necessarily read in the pristine file. We're
> talking about the case where we're *considering* the pristine file. Is
> it present? Is it usable? Should we fetch one during an "svn update"?
> etc. In these cases, we might stat() the file to compare its size
> against what is in the sqlite row, to verify the file is (still) there
> and its size is proper.
> 

*When* we have to validate the pristine file (bolded because it's
a separate discussion), we can either open the database or open or stat
the file.  The checkmode_t constants probably allow the flexibility
needed here (and if not, we'll discover it when we using them).

(In a side comment, it feels weird that they are named after "what
caller wants to do" and documented in terms of "what the implementation
really does".)

> >...
> >> >> During a single svn command, you might hit some file in a subdirectory
> >> >> as one of the arguments. The fact that it is "under" one of the
> >> >> previously-discovered wcroots is NOT sufficient. You still have to
> >> >> scan upwards to find a switched root, or an svn:external or somesuch.
> >> >
> >> > If you have .svn dirs: store the URL given to 'svn checkout' (or the key
> >> > to the WORKING_COPY table).
> >>
> >> Assume we don't. There will only be one .svn directory, at the wcroot.
> >
> > I assumed the in-tree .svn dirs would just point to the wc root
> > (basically 'ln -s ../../../../   .svn').  Are they really that bad?
> > They would save all these upwards-scans-in-the-fs all the time.
> 
> Yes, they are. People have asked for years to get rid of them.
> Primarily, it seems to come from the Mac dev community -- they have
> some tools that assume they know everything in a given directory, and
> when we throw something in there... they go wonky. Or they rebuild the
> directory and leave our .svn behind. Crap like that.
> 

If .svn is absent, we can ignore the error and fall back to scanning the 
filesystem.  If all it stores is the wc root location, we won't have lost 
anything.

> The .svn subdirectories also play hell with various "find"-based
> commands. e.g. searching for something, counting lines or files, or
> whatever.
> 

Again, I'm not convinced that this really applies if it's just a pointer 
to the wc root.  A 'grep' will only break for the wc root (and even then, 
there was the suggestion to store .svn in the wc root's parent), and 
counting lines will add you.. what?  One line per directory?


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Greg Stein <gs...@gmail.com>.
On Sun, Oct 5, 2008 at 9:30 AM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
>...
>> Well, checking the size *does* cost a stat(). If you look at the mode
>> to svn_wc__db_pristine_check(), you'll see different ways to check for
>> the presence of a pristine file. One of the modes won't even touch the
>> file -- it just relies on what is in SQLite. The "single" and "multi"
>> modes are good for "svn update" where you want to ask "hey. do I have
>> this file?" ... you can get a "no" answer really fast.
>
> Actually, I assumed that since we read the file (from disk) already, the
> cost of the stat() of the same file would be negligible.

Eh? No... we haven't necessarily read in the pristine file. We're
talking about the case where we're *considering* the pristine file. Is
it present? Is it usable? Should we fetch one during an "svn update"?
etc. In these cases, we might stat() the file to compare its size
against what is in the sqlite row, to verify the file is (still) there
and its size is proper.

>...
>> >> During a single svn command, you might hit some file in a subdirectory
>> >> as one of the arguments. The fact that it is "under" one of the
>> >> previously-discovered wcroots is NOT sufficient. You still have to
>> >> scan upwards to find a switched root, or an svn:external or somesuch.
>> >
>> > If you have .svn dirs: store the URL given to 'svn checkout' (or the key
>> > to the WORKING_COPY table).
>>
>> Assume we don't. There will only be one .svn directory, at the wcroot.
>
> I assumed the in-tree .svn dirs would just point to the wc root
> (basically 'ln -s ../../../../   .svn').  Are they really that bad?
> They would save all these upwards-scans-in-the-fs all the time.

Yes, they are. People have asked for years to get rid of them.
Primarily, it seems to come from the Mac dev community -- they have
some tools that assume they know everything in a given directory, and
when we throw something in there... they go wonky. Or they rebuild the
directory and leave our .svn behind. Crap like that.

The .svn subdirectories also play hell with various "find"-based
commands. e.g. searching for something, counting lines or files, or
whatever.

>...
>> > I don't see much difference, unless you also want to close them as soon
>> > as we don't need them (otherwise they'll all be opened soon after the
>> > start).  And then, how do we know that a certain datastore isn't needed
>> > (at least for now)?
>>
>> No plans to close them...
>
> Then in the typical case we'll have all of them open shortly after we
> start.  So I'd say, just open them all right away, and save us from the
> accounting.

Okee doke.

Cheers,
-g

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Daniel Shahaf <d....@daniel.shahaf.name>.
Greg Stein wrote on Sun, 5 Oct 2008 at 09:02 -0700:
> On Sun, Oct 5, 2008 at 8:22 AM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
> >...
> >> In RTC: if the content is never downloaded, or partially downloaded,
> >> then the row will have NULL for the STORED_SIZE. When you read the
> >> database, you'll immediately know that you don't have valid content.
> >> If some content is present, you could actually checksum it to find
> >> that you have the content, but it just didn't get recorded into the
> >> database properly.
> >
> > And even if the checksum doesn't match, you can assume that the
> > start-of-file that you have locally *is* valid, and download just the
> > rest of the file...  (if ra supported that)
> 
> Yah. That would work 99% of the time. Then there is the 1% where
> *whatever* is sitting there is just flat out wrong, so downloading
> "the rest" just gives you so much garbage. So then if you're *doubly*
> smart, then you just download the leader part. :-P
> 

:)

> >...
> >> Shoot... if you think the
> >> text base is *that* fragile, then you're gonna have to worry about
> >> race conditions. "Oh hey. The text base checksum still matches. Good.
> >> <CORRUPTION> Now, let me go ahead and use it." Ooops.
> >>
> >> So yeah. If the stored size matches what is on disk, then I'm prepare
> >> to trust it.
> >
> > I think current libsvn_wc doesn't do even that (it just trusts the bases
> > blindly?).  But, since the size check costs nothing, I see no reason not
> > to do it.
> 
> Well, checking the size *does* cost a stat(). If you look at the mode
> to svn_wc__db_pristine_check(), you'll see different ways to check for
> the presence of a pristine file. One of the modes won't even touch the
> file -- it just relies on what is in SQLite. The "single" and "multi"
> modes are good for "svn update" where you want to ask "hey. do I have
> this file?" ... you can get a "no" answer really fast.
> 

Actually, I assumed that since we read the file (from disk) already, the
cost of the stat() of the same file would be negligible.

> >...
> >> > I thought we wouldn't need to scan because each dir would know what wc
> >> > it is part of.  (If we're going to put metadata at wc-root by default,
> >>
> >> How would it know that?
> >>
> >> In the default case, a versioned dir will NOT have a .svn
> >> subdirectory. Also, there will be no central datastore. Everything
> >> will be wc-root. So given an arbitrary directory, how do you determine
> >> whether it is versioned at ALL, let alone where the wcroot is? Answer:
> >> you traverse up the directory.
> >>
> >> During a single svn command, you might hit some file in a subdirectory
> >> as one of the arguments. The fact that it is "under" one of the
> >> previously-discovered wcroots is NOT sufficient. You still have to
> >> scan upwards to find a switched root, or an svn:external or somesuch.
> >
> > If you have .svn dirs: store the URL given to 'svn checkout' (or the key
> > to the WORKING_COPY table).
> 
> Assume we don't. There will only be one .svn directory, at the wcroot.

I assumed the in-tree .svn dirs would just point to the wc root
(basically 'ln -s ../../../../   .svn').  Are they really that bad?
They would save all these upwards-scans-in-the-fs all the time.

> That may point to a central datastore, or it may be the datastore for
> the WC. I'm also in favor of an option to completely eliminate the
> .svn subdir, but then you can't move your WC around (since the central
> datastore identifies it by absolute (unchanging) path).
> 

Sounds good.

> > If have central datastore:  do the upwards-scan in the datastore (not in
> > the filesystem).  (Requires extending the DB to list switched/external
> > dirs.)
> 
> True. And if we don't find it, then default to a scan for the wcroot.
> Note that it's possible to have *some* WCs not recorded in the central
> datastore because they're configured to be wcroot-based-datastores.
> 

Yes (but see above about the scan).

> >> >> And note that we can't just have db.wc_root ... any given operation
> >> >> could cover *multiple* wc roots (think of svn:externals or switched
> >> >> subdirs).
> >> >
> >> > open_many() allows that too, no?
> >>
> >> Correct. open_many() is intended to (in one pass) find all the common
> >> pdh structures. Future requests into the API might need to look up
> >> more, but that depends on how you use the API.
> >>
> >> There is an open question: maybe just open a db handle, and then as
> >> you start using it, it will open datastores as necessary, rather than
> >> trying to do all that up front. It might simplify the API. (I don't
> >> have a good argument right now for opening all the datastores up
> >> front)
> >
> > Verifying that all of them exist and are readable?
> 
> Yah. I guess you can get an "early out" before starting any real work.
> 
> > I don't see much difference, unless you also want to close them as soon
> > as we don't need them (otherwise they'll all be opened soon after the
> > start).  And then, how do we know that a certain datastore isn't needed
> > (at least for now)?
> 
> No plans to close them...
> 

Then in the typical case we'll have all of them open shortly after we
start.  So I'd say, just open them all right away, and save us from the
accounting.

> > Also, what is 'all datastores' in the metadata-in-.svn case?  I'm sure
> > you didn't mean to open all of them recursively, but that's what we'll
> > need to be able to answer the same questions.
> 
> Yeah... there will be a lot of open databases. I'm hoping that will be okay...

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Greg Stein <gs...@gmail.com>.
On Sun, Oct 5, 2008 at 8:22 AM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
>...
>> In RTC: if the content is never downloaded, or partially downloaded,
>> then the row will have NULL for the STORED_SIZE. When you read the
>> database, you'll immediately know that you don't have valid content.
>> If some content is present, you could actually checksum it to find
>> that you have the content, but it just didn't get recorded into the
>> database properly.
>
> And even if the checksum doesn't match, you can assume that the
> start-of-file that you have locally *is* valid, and download just the
> rest of the file...  (if ra supported that)

Yah. That would work 99% of the time. Then there is the 1% where
*whatever* is sitting there is just flat out wrong, so downloading
"the rest" just gives you so much garbage. So then if you're *doubly*
smart, then you just download the leader part. :-P

>...
>> Shoot... if you think the
>> text base is *that* fragile, then you're gonna have to worry about
>> race conditions. "Oh hey. The text base checksum still matches. Good.
>> <CORRUPTION> Now, let me go ahead and use it." Ooops.
>>
>> So yeah. If the stored size matches what is on disk, then I'm prepare
>> to trust it.
>
> I think current libsvn_wc doesn't do even that (it just trusts the bases
> blindly?).  But, since the size check costs nothing, I see no reason not
> to do it.

Well, checking the size *does* cost a stat(). If you look at the mode
to svn_wc__db_pristine_check(), you'll see different ways to check for
the presence of a pristine file. One of the modes won't even touch the
file -- it just relies on what is in SQLite. The "single" and "multi"
modes are good for "svn update" where you want to ask "hey. do I have
this file?" ... you can get a "no" answer really fast.

>...
>> > I thought we wouldn't need to scan because each dir would know what wc
>> > it is part of.  (If we're going to put metadata at wc-root by default,
>>
>> How would it know that?
>>
>> In the default case, a versioned dir will NOT have a .svn
>> subdirectory. Also, there will be no central datastore. Everything
>> will be wc-root. So given an arbitrary directory, how do you determine
>> whether it is versioned at ALL, let alone where the wcroot is? Answer:
>> you traverse up the directory.
>>
>> During a single svn command, you might hit some file in a subdirectory
>> as one of the arguments. The fact that it is "under" one of the
>> previously-discovered wcroots is NOT sufficient. You still have to
>> scan upwards to find a switched root, or an svn:external or somesuch.
>
> If you have .svn dirs: store the URL given to 'svn checkout' (or the key
> to the WORKING_COPY table).

Assume we don't. There will only be one .svn directory, at the wcroot.
That may point to a central datastore, or it may be the datastore for
the WC. I'm also in favor of an option to completely eliminate the
.svn subdir, but then you can't move your WC around (since the central
datastore identifies it by absolute (unchanging) path).

> If have central datastore:  do the upwards-scan in the datastore (not in
> the filesystem).  (Requires extending the DB to list switched/external
> dirs.)

True. And if we don't find it, then default to a scan for the wcroot.
Note that it's possible to have *some* WCs not recorded in the central
datastore because they're configured to be wcroot-based-datastores.

> If you don't have .svn dirs, and don't have central datastore:  First
> time, scan up the filesystem, no other way.  Second time, do you have
> the result of scanning of the parent?  If so, you just have to check if
> you are an 'exception' to the parent (such as: completely unrelated,
> independent wc; switched; external; etc.), and wouldn't have to do
> a complete scan.

"Second time [within the process execution]". When you're in the same
process run, yah. Any upward scan will stop when it finds a wcroot, or
it finds a directory that has been seen before.

>...
>> >> And note that we can't just have db.wc_root ... any given operation
>> >> could cover *multiple* wc roots (think of svn:externals or switched
>> >> subdirs).
>> >
>> > open_many() allows that too, no?
>>
>> Correct. open_many() is intended to (in one pass) find all the common
>> pdh structures. Future requests into the API might need to look up
>> more, but that depends on how you use the API.
>>
>> There is an open question: maybe just open a db handle, and then as
>> you start using it, it will open datastores as necessary, rather than
>> trying to do all that up front. It might simplify the API. (I don't
>> have a good argument right now for opening all the datastores up
>> front)
>
> Verifying that all of them exist and are readable?

Yah. I guess you can get an "early out" before starting any real work.

> I don't see much difference, unless you also want to close them as soon
> as we don't need them (otherwise they'll all be opened soon after the
> start).  And then, how do we know that a certain datastore isn't needed
> (at least for now)?

No plans to close them...

> Also, what is 'all datastores' in the metadata-in-.svn case?  I'm sure
> you didn't mean to open all of them recursively, but that's what we'll
> need to be able to answer the same questions.

Yeah... there will be a lot of open databases. I'm hoping that will be okay...

>...

Cheers,
-g

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Daniel Shahaf <d....@daniel.shahaf.name>.
Greg Stein wrote on Sun, 5 Oct 2008 at 06:59 -0700:
> On Sun, Oct 5, 2008 at 3:41 AM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
> > Suppose you check out two wc's of the same repos at the same time.  If
> > the TEXT_BASE row was updated immediately, then as soon as the first wc
> > fetched its list of checksums of files to download, the second wouldn't
> > download any of them;
> 
> Not really. The second WC would note that all of the STORED_SIZE
> columns are NULL meaning that the content is not present, or got
> partially downloaded, or whoknowswhat. But for all intents and
> purposes, it does not have it. Thus, for safety, the second WC should
> go ahead and request all that content. (it has no idea that another
> process is doing it at the same time; coulda been last week, for all
> it knows!)
> 

Oops, right.

> > whereas, if TEXT_BASE is only updated after
> > download completes, then it's possible for both wc's to download the
> > same (identical) file simultaneously -- unnecessarily.
> 
> For safety, yes: they'll both download it. Sure, maybe that could be
> optimized, but it is no worse than today :-)
> 

This argument can save you a lot of work :-)

> >...
> >> It seems more problematic to fetch the content first, then insert a
> >> row. That's just kind of a gut feel... I don't have immediate reasons
> >> why offhand.
> >
> > (see above: I also prefer inserting the row first, but haven't good
> > reasons either)
> 
> I figured this out the other day. The scenario is an interrupted
> download. There are two choices here: add a row, then grab the content
> (RTC), or grab the content and add a row (CTR).
> 
> In RTC: if the content is never downloaded, or partially downloaded,
> then the row will have NULL for the STORED_SIZE. When you read the
> database, you'll immediately know that you don't have valid content.
> If some content is present, you could actually checksum it to find
> that you have the content, but it just didn't get recorded into the
> database properly.
> 

And even if the checksum doesn't match, you can assume that the
start-of-file that you have locally *is* valid, and download just the
rest of the file...  (if ra supported that)

> In CTR: the row is missing, so the content on disk is unknown. Maybe
> all there, or maybe a partial file. You have no information.
> 
> >> > Most of the time the pristine files don't change under our feet.  (When
> >> > they do, they're corrupted.)  So we can skip the size check and always
> >> > do only the more expensive check (calculating the checksum).  And if we
> >> > skip the size check, we won't need stored_size in the database.
> >>
> >> We can do a quick apr_stat() to see if the file is present and the
> >> right size, and have a high confidence that it is usable.
> >
> > I wouldn't trust the size alone: it's too easy to run 'find | xargs sed'
> > that doesn't modify the sizes.  That's why I assumed we would checksum
> > the base file before declaring it usable.
> 
> Well... I don't think we go to that extent today, so I'm not prepared
> to distrust the text base to that level.

Good point, I'm convinced.

> Shoot... if you think the
> text base is *that* fragile, then you're gonna have to worry about
> race conditions. "Oh hey. The text base checksum still matches. Good.
> <CORRUPTION> Now, let me go ahead and use it." Ooops.
> 
> So yeah. If the stored size matches what is on disk, then I'm prepare
> to trust it.
> 

I think current libsvn_wc doesn't do even that (it just trusts the bases
blindly?).  But, since the size check costs nothing, I see no reason not
to do it.

> Note that svn_wc__db_pristine_check() *does* have a mode that will
> re-run a checksum. We can use that mode from "svn cleanup" or
> somethign.
> 
> >> If the stored_size is not event present, then the file is not going to
> >> be there, or it has only been partially-fetched.
> >
> > I agree that it's useful for the partially-fetched case.  But in that case
> > a boolean would work as well (just store an arbitrary non-NULL value at
> > the point you would store the size).
> 
> If the text base's size changes, then we have a fast-path exit to
> "corrupt", so hey... SIZE DOES MATTER.
> 
> :-P
> 
> > The rest is pure logic:  since I don't trust the size as corruption
> > indicator and since I think a boolean would work as well as the size for
> > detecting the partial-fetch case, therefore I am not yet convinced that
> > we need to store the size at all.
> >
> > (You're welcome to convince me otherwise, of course :))
> 
> If the size is the same, then we can always run a checksum. But we can
> skip the checksum if whatever happened to the text base has changed
> the size. If we want to go nuts on corruption detection, then we could
> also store a timestamp.
> 
> >...
> >> > The metadata location (per-dir, wc root, etc.) is typically per-wc, not
> >> > per-versioned-dir.  As the code is, we'll just set a key in the hash for
> >> > every single directory in the wc, recursively.  But we could store the
> >> > information (whether the pristine dir is stored in-place or at the wc
> >> > root) just for the wc root and let deeper dirs inherit its setting.
> >>
> >> Whenever an operation hits a subdirectory, we don't want to scan
> >> upwards to find the wc-root. We want to just *go* to where the data
> >> is. Thus, we have pdh.base_dir.
> >
> > I thought we wouldn't need to scan because each dir would know what wc
> > it is part of.  (If we're going to put metadata at wc-root by default,
> 
> How would it know that?
> 
> In the default case, a versioned dir will NOT have a .svn
> subdirectory. Also, there will be no central datastore. Everything
> will be wc-root. So given an arbitrary directory, how do you determine
> whether it is versioned at ALL, let alone where the wcroot is? Answer:
> you traverse up the directory.
> 
> During a single svn command, you might hit some file in a subdirectory
> as one of the arguments. The fact that it is "under" one of the
> previously-discovered wcroots is NOT sufficient. You still have to
> scan upwards to find a switched root, or an svn:external or somesuch.
> 

If you have .svn dirs: store the URL given to 'svn checkout' (or the key
to the WORKING_COPY table).

If have central datastore:  do the upwards-scan in the datastore (not in
the filesystem).  (Requires extending the DB to list switched/external
dirs.)

If you don't have .svn dirs, and don't have central datastore:  First
time, scan up the filesystem, no other way.  Second time, do you have
the result of scanning of the parent?  If so, you just have to check if
you are an 'exception' to the parent (such as: completely unrelated,
independent wc; switched; external; etc.), and wouldn't have to do
a complete scan.

Of course, if you don't have the parent's scan results available, you
just have to scan all the way up.  (And then you'd have the same problem
in every operation that needs the wc root, not just in finding the
pristine/ files.)

> > having a cheap way to find it seems natural.)  And the scan would be in
> > the hash, not in the disk (except for the first scan).  (Lastly, isn't
> > there a recursion a few layers above us that we can take advantage of?)
> 
> Once you fill the hash, then yes: it'll be fast. And as you smack new
> directories, as they traverse upwards, you may run into a
> previously-known directory and terminate your scan.
> 
> >> I also didn't envision proactively setting subdir values, but lazily
> >> filling them in. Today, the code will create a new pdh structure since
> >> we're doing per-versioned-dir base_dir values.
> >
> > Lazy filling or not, by the end of the checkout you'll have a hash key
> > for every versioned dir in the wc just checked out... right?  Couldn't
> > memory usage, etc., become significant?
> 
> We've previously stated that an in-memory map of directories (and even
> files! ugh) is okay. Take a look at the commit process some time: it
> holds all that crap in memory. It is *never* okay to store a file's
> contents in memory, but we've been okay with metadata.
> 

I didn't know that.  Guess it's fine, then, by precedent.

> In the long run, we'll have a bunch of directory paths mapping to a
> single pdh structure, so it "shouldn't be too bad".
> 
> >...
> >> And note that we can't just have db.wc_root ... any given operation
> >> could cover *multiple* wc roots (think of svn:externals or switched
> >> subdirs).
> >
> > open_many() allows that too, no?
> 
> Correct. open_many() is intended to (in one pass) find all the common
> pdh structures. Future requests into the API might need to look up
> more, but that depends on how you use the API.
> 

> There is an open question: maybe just open a db handle, and then as
> you start using it, it will open datastores as necessary, rather than
> trying to do all that up front. It might simplify the API. (I don't
> have a good argument right now for opening all the datastores up
> front)
> 

Verifying that all of them exist and are readable?

I don't see much difference, unless you also want to close them as soon
as we don't need them (otherwise they'll all be opened soon after the
start).  And then, how do we know that a certain datastore isn't needed
(at least for now)?

Also, what is 'all datastores' in the metadata-in-.svn case?  I'm sure
you didn't mean to open all of them recursively, but that's what we'll
need to be able to answer the same questions.

> >...
> >> Note that if the result is HOME or GIVEN_PATH, then all the hash
> >> values would point to the same PDH structure. Well... at least for
> >> a given WC. I envision an eventual possibility of different configs
> >> for different WCs. For example, a WC on an NFS mount might be WC_ROOT,
> >> while your personal WCs are all GIVEN_PATH.
> >
> > The part that surprises me is "eventual".  Are you saying we won't be able
> > to operate on metadata-at-.svn-dirs and on metadata-at-wc-root wc's in the
> > same operation ('svn up wc1 wc2') or in subsequent operations under the
> > same environment ('svn up wc1; svn up wc2') by default?
> 
> In one operation, some of the datastores may be wcroot, and others
> centralized. I don't know what the commands will look like or the
> configs or anything to get that kind of granularity, which is why I
> said "eventual". The APIs definitely support metadata-per-wc to be
> located anywhere, and to mix/match that at will.

okay.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Greg Stein <gs...@gmail.com>.
On Sun, Oct 5, 2008 at 3:41 AM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
> Greg Stein wrote on Fri, 3 Oct 2008 at 03:50 -0700:
>> On Thu, Oct 2, 2008 at 11:39 PM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
>> >>     - place the checksum directly into this table, rather than a "text_id"
>> >>       field. due to operational issues, the text_id may not be a satisfiable
>> >>       foreign key (we may not want to insert a row).
>> >
>> > Can you explain this scenario?  When do you see TEXT_ID not a valid
>> > foreign key *and* CHECKSUM a valid foreign key (at the same time)?
>> > Before this change, the schema was "id PRIMARY KEY, checksum NOT NULL".
>>
>> CHECKSUM might not be valid, but it is a known quantity when the row
>> is inserted into NODE. Say, during a checkout, I get all the metadata
>> for the file, but I haven't started to fetch it. So we create a row in
>> NODE. Later, as we begin the fetching of the content, then we insert a
>> row into TEXT_BASE.
>
> IOW, the foreign key won't be enforced by sqlite, in this case.

That was my plan, but per our conversion on IRC, if we insert into
TEXT_BASE as soon as we know the checksum, then we can establish a
foreign-key relationship and a hard integrity constraint. Since the
system is already designed to support a row in TEXT_BASE with no
corresponding filesystem content, we may as well store the knowledge
of the checksum while we have it.

Another outcome of that conversion was: we'll definitely stick to
CHECKSUM as the primary key. Since that is unique, and there are no
(appreciable) performance gains to using a plain numeric ID for the
key... then we'll go for clarity/simplicity and stick to CHECKSUM.

>> If we kept it as an ID, then we'd have to create a row in TEXT_BASE
>> well before any intent of actually fetching that. Now, that said, the
>> TEXT_BASE.STORED_SIZE would be NULL, so we already know it is not
>> there or is incomplete. So that might actually be fine.
>
> So, the question is whether to create the TEXT_BASE row before or after
> fetching the file itself.

As above... we'll insert immediately.

> Suppose you check out two wc's of the same repos at the same time.  If
> the TEXT_BASE row was updated immediately, then as soon as the first wc
> fetched its list of checksums of files to download, the second wouldn't
> download any of them;

Not really. The second WC would note that all of the STORED_SIZE
columns are NULL meaning that the content is not present, or got
partially downloaded, or whoknowswhat. But for all intents and
purposes, it does not have it. Thus, for safety, the second WC should
go ahead and request all that content. (it has no idea that another
process is doing it at the same time; coulda been last week, for all
it knows!)

> whereas, if TEXT_BASE is only updated after
> download completes, then it's possible for both wc's to download the
> same (identical) file simultaneously -- unnecessarily.

For safety, yes: they'll both download it. Sure, maybe that could be
optimized, but it is no worse than today :-)

>...
>> It seems more problematic to fetch the content first, then insert a
>> row. That's just kind of a gut feel... I don't have immediate reasons
>> why offhand.
>
> (see above: I also prefer inserting the row first, but haven't good
> reasons either)

I figured this out the other day. The scenario is an interrupted
download. There are two choices here: add a row, then grab the content
(RTC), or grab the content and add a row (CTR).

In RTC: if the content is never downloaded, or partially downloaded,
then the row will have NULL for the STORED_SIZE. When you read the
database, you'll immediately know that you don't have valid content.
If some content is present, you could actually checksum it to find
that you have the content, but it just didn't get recorded into the
database properly.

In CTR: the row is missing, so the content on disk is unknown. Maybe
all there, or maybe a partial file. You have no information.

>> > Most of the time the pristine files don't change under our feet.  (When
>> > they do, they're corrupted.)  So we can skip the size check and always
>> > do only the more expensive check (calculating the checksum).  And if we
>> > skip the size check, we won't need stored_size in the database.
>>
>> We can do a quick apr_stat() to see if the file is present and the
>> right size, and have a high confidence that it is usable.
>
> I wouldn't trust the size alone: it's too easy to run 'find | xargs sed'
> that doesn't modify the sizes.  That's why I assumed we would checksum
> the base file before declaring it usable.

Well... I don't think we go to that extent today, so I'm not prepared
to distrust the text base to that level. Shoot... if you think the
text base is *that* fragile, then you're gonna have to worry about
race conditions. "Oh hey. The text base checksum still matches. Good.
<CORRUPTION> Now, let me go ahead and use it." Ooops.

So yeah. If the stored size matches what is on disk, then I'm prepare
to trust it.

Note that svn_wc__db_pristine_check() *does* have a mode that will
re-run a checksum. We can use that mode from "svn cleanup" or
somethign.

>> If the stored_size is not event present, then the file is not going to
>> be there, or it has only been partially-fetched.
>
> I agree that it's useful for the partially-fetched case.  But in that case
> a boolean would work as well (just store an arbitrary non-NULL value at
> the point you would store the size).

If the text base's size changes, then we have a fast-path exit to
"corrupt", so hey... SIZE DOES MATTER.

:-P

> The rest is pure logic:  since I don't trust the size as corruption
> indicator and since I think a boolean would work as well as the size for
> detecting the partial-fetch case, therefore I am not yet convinced that
> we need to store the size at all.
>
> (You're welcome to convince me otherwise, of course :))

If the size is the same, then we can always run a checksum. But we can
skip the checksum if whatever happened to the text base has changed
the size. If we want to go nuts on corruption detection, then we could
also store a timestamp.

>...
>> > The metadata location (per-dir, wc root, etc.) is typically per-wc, not
>> > per-versioned-dir.  As the code is, we'll just set a key in the hash for
>> > every single directory in the wc, recursively.  But we could store the
>> > information (whether the pristine dir is stored in-place or at the wc
>> > root) just for the wc root and let deeper dirs inherit its setting.
>>
>> Whenever an operation hits a subdirectory, we don't want to scan
>> upwards to find the wc-root. We want to just *go* to where the data
>> is. Thus, we have pdh.base_dir.
>
> I thought we wouldn't need to scan because each dir would know what wc
> it is part of.  (If we're going to put metadata at wc-root by default,

How would it know that?

In the default case, a versioned dir will NOT have a .svn
subdirectory. Also, there will be no central datastore. Everything
will be wc-root. So given an arbitrary directory, how do you determine
whether it is versioned at ALL, let alone where the wcroot is? Answer:
you traverse up the directory.

During a single svn command, you might hit some file in a subdirectory
as one of the arguments. The fact that it is "under" one of the
previously-discovered wcroots is NOT sufficient. You still have to
scan upwards to find a switched root, or an svn:external or somesuch.

> having a cheap way to find it seems natural.)  And the scan would be in
> the hash, not in the disk (except for the first scan).  (Lastly, isn't
> there a recursion a few layers above us that we can take advantage of?)

Once you fill the hash, then yes: it'll be fast. And as you smack new
directories, as they traverse upwards, you may run into a
previously-known directory and terminate your scan.

>> I also didn't envision proactively setting subdir values, but lazily
>> filling them in. Today, the code will create a new pdh structure since
>> we're doing per-versioned-dir base_dir values.
>
> Lazy filling or not, by the end of the checkout you'll have a hash key
> for every versioned dir in the wc just checked out... right?  Couldn't
> memory usage, etc., become significant?

We've previously stated that an in-memory map of directories (and even
files! ugh) is okay. Take a look at the commit process some time: it
holds all that crap in memory. It is *never* okay to store a file's
contents in memory, but we've been okay with metadata.

In the long run, we'll have a bunch of directory paths mapping to a
single pdh structure, so it "shouldn't be too bad".

>...
>> And note that we can't just have db.wc_root ... any given operation
>> could cover *multiple* wc roots (think of svn:externals or switched
>> subdirs).
>
> open_many() allows that too, no?

Correct. open_many() is intended to (in one pass) find all the common
pdh structures. Future requests into the API might need to look up
more, but that depends on how you use the API.

There is an open question: maybe just open a db handle, and then as
you start using it, it will open datastores as necessary, rather than
trying to do all that up front. It might simplify the API. (I don't
have a good argument right now for opening all the datastores up
front)

>...
>> Note that if the result is HOME or GIVEN_PATH, then all the hash
>> values would point to the same PDH structure. Well... at least for
>> a given WC. I envision an eventual possibility of different configs
>> for different WCs. For example, a WC on an NFS mount might be WC_ROOT,
>> while your personal WCs are all GIVEN_PATH.
>
> The part that surprises me is "eventual".  Are you saying we won't be able
> to operate on metadata-at-.svn-dirs and on metadata-at-wc-root wc's in the
> same operation ('svn up wc1 wc2') or in subsequent operations under the
> same environment ('svn up wc1; svn up wc2') by default?

In one operation, some of the datastores may be wcroot, and others
centralized. I don't know what the commands will look like or the
configs or anything to get that kind of granularity, which is why I
said "eventual". The APIs definitely support metadata-per-wc to be
located anywhere, and to mix/match that at will.

>...

Cheers,
-g

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Daniel Shahaf <d....@daniel.shahaf.name>.
Greg Stein wrote on Fri, 3 Oct 2008 at 03:50 -0700:
> On Thu, Oct 2, 2008 at 11:39 PM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
> >>     - place the checksum directly into this table, rather than a "text_id"
> >>       field. due to operational issues, the text_id may not be a satisfiable
> >>       foreign key (we may not want to insert a row).
> >
> > Can you explain this scenario?  When do you see TEXT_ID not a valid
> > foreign key *and* CHECKSUM a valid foreign key (at the same time)?
> > Before this change, the schema was "id PRIMARY KEY, checksum NOT NULL".
> 
> CHECKSUM might not be valid, but it is a known quantity when the row
> is inserted into NODE. Say, during a checkout, I get all the metadata
> for the file, but I haven't started to fetch it. So we create a row in
> NODE. Later, as we begin the fetching of the content, then we insert a
> row into TEXT_BASE.
> 

IOW, the foreign key won't be enforced by sqlite, in this case.

> If we kept it as an ID, then we'd have to create a row in TEXT_BASE
> well before any intent of actually fetching that. Now, that said, the
> TEXT_BASE.STORED_SIZE would be NULL, so we already know it is not
> there or is incomplete. So that might actually be fine.

So, the question is whether to create the TEXT_BASE row before or after
fetching the file itself.

Suppose you check out two wc's of the same repos at the same time.  If
the TEXT_BASE row was updated immediately, then as soon as the first wc
fetched its list of checksums of files to download, the second wouldn't
download any of them; whereas, if TEXT_BASE is only updated after
download completes, then it's possible for both wc's to download the
same (identical) file simultaneously -- unnecessarily.

That said, I probably would download one wc, duplicate it, and run
'switch' on the duplicate, so that use-case might be invalid.  But
I suspect recording early the intent to create the base file might
have other benefits (even if I can think of none now).

> (and, in which case, we could go back to an ID style keying).
> 

We'll still need the checksums to be unique (to store them in the
pristine/ dir).  If the ID-keying is faster we can use it, but most
other arguments I think of are in favour of using the checksums
directly.  (Speaking of 'faster', it might be faster to store the
checksum in the db as an integer rather than as TEXT?)

> Thoughts?
> 
> >> +  /* ### used to verify the pristine file is "proper". NULL if unknown,
> >> +     ### and (thus) the pristine copy is incomplete/unusable. */
> >> +  stored_size  INTEGER,
> >
> > Do we need this?
> 
> Yes. Remember that we do not have transactions across the table and
> the filesystem.

Noted.

> My plan is that we insert the row first, *then* we
> fetch the file into the filesystem. At the completion of that event,
> we stash the size back into the table. The presence of the size tells
> us what it *should* be (detect corruption), and it tells us that the
> file should be there [now].
> 

I didn't catch that it also functions as a "download complete" flag.

> It seems more problematic to fetch the content first, then insert a
> row. That's just kind of a gut feel... I don't have immediate reasons
> why offhand.
> 

(see above: I also prefer inserting the row first, but haven't good
reasons either)

> > Most of the time the pristine files don't change under our feet.  (When
> > they do, they're corrupted.)  So we can skip the size check and always
> > do only the more expensive check (calculating the checksum).  And if we
> > skip the size check, we won't need stored_size in the database.
> 
> We can do a quick apr_stat() to see if the file is present and the
> right size, and have a high confidence that it is usable.
> 

I wouldn't trust the size alone: it's too easy to run 'find | xargs sed'
that doesn't modify the sizes.  That's why I assumed we would checksum
the base file before declaring it usable.

> If the stored_size is not event present, then the file is not going to
> be there, or it has only been partially-fetched.
> 

I agree that it's useful for the partially-fetched case.  But in that case
a boolean would work as well (just store an arbitrary non-NULL value at
the point you would store the size).


The rest is pure logic:  since I don't trust the size as corruption
indicator and since I think a boolean would work as well as the size for
detecting the partial-fetch case, therefore I am not yet convinced that
we need to store the size at all.

(You're welcome to convince me otherwise, of course :))

> >> -  refcount INTEGER NOT NULL
> >> +  refcount  INTEGER NOT NULL
> >
> > This value can be derived from the other tables, can't it?  (See?  Who
> > said whitespace-only changes were useless?)
> 
> I realized this while driving yesterday, yes.
> 
> Right now, I only plan to have a TEXT_BASE table, which means I do
> actually need a refcount. When I later add a NODE and NODE_CHANGES
> table, then I can drop the refcount and just use COUNT(*) on the other
> tables. Which means that I also want to add an index on the CHECKSUM
> column in those tables. (or ID if we decide to switch back)
> 
> Alternatively, keep the refcount and use SQLite triggers to maintain
> it. But I think a COUNT(*) should be more than fast enough against an
> indexed column.
> 

Agreed.

> >> svn_error_t *
> >> svn_wc__db_pristine_dirhandle(svn_wc__db_pdh_t **pdh,
> >>                               svn_wc__db_t *db,
> >>                               const char *dirpath,
> >>                               apr_pool_t *result_pool,
> >>                               apr_pool_t *scratch_pool)
> >> {
> >>   /* ### need to fix this up. we'll probably get called with a subdirectory
> >>      ### of some dirpath that we opened originally. that means we probably
> >>      ### won't have the subdir in the hash table. need to be able to
> >>      ### incrementally grow the hash of per-dir structures. */
> >>
> >>   *pdh = apr_hash_get(db->dir_data, dirpath, APR_HASH_KEY_STRING);
> >>
> >>   return SVN_NO_ERROR;
> >> }
> >
> > The metadata location (per-dir, wc root, etc.) is typically per-wc, not
> > per-versioned-dir.  As the code is, we'll just set a key in the hash for
> > every single directory in the wc, recursively.  But we could store the
> > information (whether the pristine dir is stored in-place or at the wc
> > root) just for the wc root and let deeper dirs inherit its setting.
> 
> Whenever an operation hits a subdirectory, we don't want to scan
> upwards to find the wc-root. We want to just *go* to where the data
> is. Thus, we have pdh.base_dir.
> 

I thought we wouldn't need to scan because each dir would know what wc
it is part of.  (If we're going to put metadata at wc-root by default,
having a cheap way to find it seems natural.)  And the scan would be in
the hash, not in the disk (except for the first scan).  (Lastly, isn't
there a recursion a few layers above us that we can take advantage of?)

> I also didn't envision proactively setting subdir values, but lazily
> filling them in. Today, the code will create a new pdh structure since
> we're doing per-versioned-dir base_dir values.

Lazy filling or not, by the end of the checkout you'll have a hash key
for every versioned dir in the wc just checked out... right?  Couldn't
memory usage, etc., become significant?

> In the future, yes, they could all share the same pdh.
> 

*nod*  Good point.

> And note that we can't just have db.wc_root ... any given operation
> could cover *multiple* wc roots (think of svn:externals or switched
> subdirs).
> 

open_many() allows that too, no?

> > Also, for the values, why use strings?  An enum should cover the 2-3
> > most common cases ("in ./.svn", "in wc root" (we know where it is),
> > "in ~/.subversion").
> 
> Given a directory, you don't know where the wc-root is, without
> looking for it. The ~/.subversion/ or /var/svn options are based on
> the config data, so you'd have to scrawl through there if your enum
> said HOME or GIVEN_PATH. Rather than scrawl each time, again: just
> drop in the result.
> 

See above.  I assumed there ought to be an easy way to avoid reading the
env (config data, wc root, etc.) more than once.  (I didn't try to think
how that 'easy way' might work, I simply assumed its existence.)

> Note that if the result is HOME or GIVEN_PATH, then all the hash
> values would point to the same PDH structure. Well... at least for
> a given WC. I envision an eventual possibility of different configs
> for different WCs. For example, a WC on an NFS mount might be WC_ROOT,
> while your personal WCs are all GIVEN_PATH.
> 

The part that surprises me is "eventual".  Are you saying we won't be able
to operate on metadata-at-.svn-dirs and on metadata-at-wc-root wc's in the
same operation ('svn up wc1 wc2') or in subsequent operations under the
same environment ('svn up wc1; svn up wc2') by default?

> >> svn_error_t *
> >> svn_wc__db_pristine_decref(int *new_refcount,
> >>                            svn_wc__db_pdh_t *pdh,
> >>                            svn_checksum_t *checksum,
> >>                            apr_pool_t *scratch_pool)
> >> {
> >>   return svn_error__malfunction(__FILE__, __LINE__, "Not implemented.");
> >> }
> >
> > Interesting spelling of the error handling.
> 
> hehe. Figured I'd go there rather than abort() :-)
> 
> Note that these functions will probably just go away. It looks like we
> don't have to do separate reference count management.
> 

Noted, but irrelevant (the same spelling of abort() is used in other
functions).

> Cheers,
> -g
> 

Daniel
(who just learnt that not only Vim, but Ctags as well, must be manually
taught that *.sql3 is an SQL file.)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: svn commits: r33357, r33358

Posted by Greg Stein <gs...@gmail.com>.
Thanks for the review! Comments inline.

On Thu, Oct 2, 2008 at 11:39 PM, Daniel Shahaf <d....@daniel.shahaf.name> wrote:
>
> Took me a few days to get to these...
>
> r33357:
>> Continued evoluation of the metadata schema.
>...
>>     - place the checksum directly into this table, rather than a "text_id"
>>       field. due to operational issues, the text_id may not be a satisfiable
>>       foreign key (we may not want to insert a row).
>
> Can you explain this scenario?  When do you see TEXT_ID not a valid
> foreign key *and* CHECKSUM a valid foreign key (at the same time)?
> Before this change, the schema was "id PRIMARY KEY, checksum NOT NULL".

CHECKSUM might not be valid, but it is a known quantity when the row
is inserted into NODE. Say, during a checkout, I get all the metadata
for the file, but I haven't started to fetch it. So we create a row in
NODE. Later, as we begin the fetching of the content, then we insert a
row into TEXT_BASE.

If we kept it as an ID, then we'd have to create a row in TEXT_BASE
well before any intent of actually fetching that. Now, that said, the
TEXT_BASE.STORED_SIZE would be NULL, so we already know it is not
there or is incomplete. So that might actually be fine. (and, in which
case, we could go back to an ID style keying).

Thoughts?

>...
>> +CREATE TABLE TEXT_BASE (
>>    /* ### the hash algorithm (MD5 or SHA-1) is encoded in this value */
>> -  checksum  TEXT NOT NULL,
>> -
>> -  /* ### enumerated values specifying type of compression */
>> -  compression  INTEGER NOT NULL,
>> +  checksum  TEXT NOT NULL PRIMARY KEY,
>
> Okay.  A certain text-base may appear more than once (if we change the
> hash algorithm used), but this won't break anything.  (The row may be
> shared, so we'll have to create a new one rather than just modify the
> CHECKSUM column's value.)

Right.

>> +  /* ### used to verify the pristine file is "proper". NULL if unknown,
>> +     ### and (thus) the pristine copy is incomplete/unusable. */
>> +  stored_size  INTEGER,
>
> Do we need this?

Yes. Remember that we do not have transactions across the table and
the filesystem. My plan is that we insert the row first, *then* we
fetch the file into the filesystem. At the completion of that event,
we stash the size back into the table. The presence of the size tells
us what it *should* be (detect corruption), and it tells us that the
file should be there [now].

It seems more problematic to fetch the content first, then insert a
row. That's just kind of a gut feel... I don't have immediate reasons
why offhand.

> Most of the time the pristine files don't change under our feet.  (When
> they do, they're corrupted.)  So we can skip the size check and always
> do only the more expensive check (calculating the checksum).  And if we
> skip the size check, we won't need stored_size in the database.

We can do a quick apr_stat() to see if the file is present and the
right size, and have a high confidence that it is usable.

If the stored_size is not event present, then the file is not going to
be there, or it has only been partially-fetched.

>> -  refcount INTEGER NOT NULL
>> +  refcount  INTEGER NOT NULL
>
> This value can be derived from the other tables, can't it?  (See?  Who
> said whitespace-only changes were useless?)

I realized this while driving yesterday, yes.

Right now, I only plan to have a TEXT_BASE table, which means I do
actually need a refcount. When I later add a NODE and NODE_CHANGES
table, then I can drop the refcount and just use COUNT(*) on the other
tables. Which means that I also want to add an index on the CHECKSUM
column in those tables. (or ID if we decide to switch back)

Alternatively, keep the refcount and use SQLite triggers to maintain
it. But I think a COUNT(*) should be more than fast enough against an
indexed column.

> r33358:
>> --- trunk/subversion/libsvn_wc/wc_db.h        2008/09/30 04:39:20     33357
>> +++ trunk/subversion/libsvn_wc/wc_db.h        2008/09/30 04:58:37     33358
>> +/* ### @a new_refcount may be NULL */
>> +svn_error_t *
>> +svn_wc__db_pristine_decref(int *new_refcount,
>> +                           svn_wc__db_pdh_t *pdh,
>> +                           svn_checksum_t *checksum,
>
> So, sometimes 'svn_checksum_t *checksum' is the primary key of the
> operation, and sometimes it just describes a file passed in another arg.
> Personally I'd find it easier to read if it were named differently
> depending on its role -- e.g., here it might be named 'svn_checksum_t
> *text_base_id'.  That conveys more information than calling it 'checksum'.

Good point.

>...
>>       /* If the file is special, then we need to refer to the encapsulating
>
> Contrary to the comment, the boolean variable 'special' is not used in
> the function.

True. But the important part was that *if* it is a symlink, then it
was not resolved. And flags come back as svn_node_file/TRUE, and given
that it isn't "dir", then we get the dirname and go from there.

I'll clarify the comment.

>...
>> svn_error_t *
>> svn_wc__db_pristine_dirhandle(svn_wc__db_pdh_t **pdh,
>>                               svn_wc__db_t *db,
>>                               const char *dirpath,
>>                               apr_pool_t *result_pool,
>>                               apr_pool_t *scratch_pool)
>> {
>>   /* ### need to fix this up. we'll probably get called with a subdirectory
>>      ### of some dirpath that we opened originally. that means we probably
>>      ### won't have the subdir in the hash table. need to be able to
>>      ### incrementally grow the hash of per-dir structures. */
>>
>>   *pdh = apr_hash_get(db->dir_data, dirpath, APR_HASH_KEY_STRING);
>>
>>   return SVN_NO_ERROR;
>> }
>
> The metadata location (per-dir, wc root, etc.) is typically per-wc, not
> per-versioned-dir.  As the code is, we'll just set a key in the hash for
> every single directory in the wc, recursively.  But we could store the
> information (whether the pristine dir is stored in-place or at the wc
> root) just for the wc root and let deeper dirs inherit its setting.

Whenever an operation hits a subdirectory, we don't want to scan
upwards to find the wc-root. We want to just *go* to where the data
is. Thus, we have pdh.base_dir.

I also didn't envision proactively setting subdir values, but lazily
filling them in. Today, the code will create a new pdh structure since
we're doing per-versioned-dir base_dir values. In the future, yes,
they could all share the same pdh.

And note that we can't just have db.wc_root ... any given operation
could cover *multiple* wc roots (think of svn:externals or switched
subdirs).

> Also, for the values, why use strings?  An enum should cover the 2-3
> most common cases ("in ./.svn", "in wc root" (we know where it is),
> "in ~/.subversion").

Given a directory, you don't know where the wc-root is, without
looking for it. The ~/.subversion/ or /var/svn options are based on
the config data, so you'd have to scrawl through there if your enum
said HOME or GIVEN_PATH. Rather than scrawl each time, again: just
drop in the result.

Note that if the result is HOME or GIVEN_PATH, then all the hash
values would point to the same PDH structure. Well... at least for a
given WC. I envision an eventual possibility of different configs for
different WCs. For example, a WC on an NFS mount might be WC_ROOT,
while your personal WCs are all GIVEN_PATH.

>> svn_error_t *
>> svn_wc__db_pristine_decref(int *new_refcount,
>>                            svn_wc__db_pdh_t *pdh,
>>                            svn_checksum_t *checksum,
>>                            apr_pool_t *scratch_pool)
>> {
>>   return svn_error__malfunction(__FILE__, __LINE__, "Not implemented.");
>> }
>
> Interesting spelling of the error handling.

hehe. Figured I'd go there rather than abort() :-)

Note that these functions will probably just go away. It looks like we
don't have to do separate reference count management.

Cheers,
-g

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org