Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsFormsForms ProgrammingQueriesModules / DAO / VBAReports / PrintingMacrosDatabase DesignSecurityConversionImporting / LinkingSQL Server / ADPMultiuser / NetworkingReplicationSetup / ConfigurationDeveloper ToolkitsActiveX ControlsNew UsersGeneral 1General 2
Access DirectoryToolsTutorialsUser Groups
Related Topics
SQL ServerOther DB ProductsMS OfficeMore Topics ...

MS Access Forum / Replication / June 2007

Tip: Looking for answers? Try searching our database.

Homegrown synchronization

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
rdemyan@hotmail.com - 23 Dec 2006 16:53 GMT
I'm concerned about the lack of support by Microsoft for database
replication and indirect synchronization.  They don't even supply
replication manager any more.  In our tough IT environment, they don't
allow unapproved third-party software (like TSI Synchronizer). Further
IT has not approved the Jet Synchronizer for use and it currently
cannot be installed. Even if they do eventually allow it, they may at
some point in the future take it away.  Also since the Replication
Manager is now no longer provided with Office Professional 2003, I
seriously doubt they will allow its installation even if they can get a
copy.   Although Jet direct synchronization is a possibility, a number
of users are on WANs so corruption is possible (many would say
inevitable).

The institution I work with is at the forefront of network security.
Since my app is only used by a relatively small number of people and is
not mission critical, they could care less about our requirements.  Who
knows what they will do in the future.  So, I'm thinking about creating
my own indirect synchronization code.  Although it will be major work,
at least I will own it and nobody will be able to take it away.  My
thoughts are to do it differently than Jet and to not have any features
regarding synchronization of table design or synchronization
scheduling.

So:

1) Scrap Jet Replication and Synchronization. This is not a problem
since we've only been testing it for use.  It was never implemented in
the existing production environment.  Start with an UNREPLICATED
back-end file.  Each workstation (including laptops) will have its own
"local" backend.  In addition, there will be a backend on a common
server that all workstations can connect to.

2) All back-end tables currently have a time-stamp field which is
modified if data in the record is changed.

3) Create a "model" database that contains all of the tables from the
back end but is EMPTY of data.  This "model" database will be copied to
each user's workstation and be part of my application's files.  When a
user updates data on their local backend, code in my app will create a
temporary database from this "model": a temporary updateDB (temp
updateDB).  The temp updateDB will contain all the tables from the
backend, but will be initially empty of data.  The code then will write
to the temp updateDB only the records that were added/modified/deleted.
The tables in the temp updateDB, however, will include an extra field
used by "import" code to decide what to do with the records in a table
in the temp updateDB.  I think I can keep this as a Yes/No field.  If
'Yes' then when data from the temp updateDB is imported into a backend,
the record is deleted.  If 'No' then the record is an update to an
existing record or addition of a new record.

How does my application know what data to write to the temp updateDB?
Good question.  My thinking right now is that I can just use the
TimeStamp field in each table in the backend.  For additions and
updates, copy only records where the TimeStamp has today's date in it
(or some such algorithm).  Any deletions would probably need to
immediately be written to the temp updateDB (since that record is
removed from the backend tables).   This is an area that needs more
thought in order to keep it simple, but I think is definately doable.

4) My app will look to add records to the temp updateDB when the app
closes (deletions would presumably already have been added).  Once the
temp updateDB is filled with the updated/modified/deleted data and the
Yes/No flag set for each record, then the app will copy the temp
updateDB from the local workstation to a common server location that
all workstations can see.

So, the idea is that the temp updateDB created by the app on local
workstations should in the vast majority of cases be relatively small
and easily/quickly copied to the common server folder.  It will be
named with the user's name and a date (down to the second).  BTW:  The
reason for using a model DB is that it should be quicker to simply
create an instance of a model database, rather than creating an entire
temporary DB from code (besides I've got a lot of other coding to do!).
I already do it this way for my normal temporary database that my app
uses.  In two years, this techniques has always worked as intended and
never failed.

5)  When my app starts up, it will look for temp updateDBs in a folder
on the server.  If it finds any that have not already been processed
(based on prior logs in a frontend table), it will copy them from the
server to the workstation.  Then code in the app will transfer
"update/add" data from each table in the temp updateDB(s), copied over
from the server, to the equivalent local backend table.  If the flag
for the record in the table in the  temp updateDB is set to 'Yes' that
record will be deleted from the local backend.  As noted, my app will
contain a front-end table that will log the filename of each temp
updateDB that was imported as well as the import date.

So, I think this can work for updating workstations.  It's an indirect
synchronization scheme because the temp updateDB and the local back-end
file are only opened locally for updating of the local back-end file.
Scheduling is not needed.  When the app opens it will look for temp
updateDB(s) to IMPORT and when it closes it will look to EXPORT any
table changes made by that user into a temp updateDB that will be
copied to the server.  Again synchronizing table design changes is not
critical. There are other ways to handle this.  If we change the table
design, then we will do it to the most current copy we can find and run
code to take all temp updateDB files on the server and import them.
Most of these updates will likely fail due to key violations.  My app
launcher already looks for updates to my app located on the server and
will download them to the workstation.  It is a simple matter to also
force downloading of fresh copies of the backend from the server.
Further, in this scenario, even if the supposedly updated backend on
the server does not truly have all current data, a new copy of my app
will have an empty frontend table that logged previous imports of temp
updateDBs.  So it will try again to import temp updateDBs once it is on
the workstation.  Most if not all will fail due to key violations, but
it should ensure that the new backend file is updated.

HOWEVER,  I still need to have a current copy of the backend on the
server.  My app launcher allows users to download a copy of the backend
from the server (in case it ever gets corrupted,or if we've changed the
table designs, or if they haven't logged on in 30 to 60 days).

But I'm unclear on how to keep the copy of the backend on the server
up-to-date.  I guess I will need to have one or more of the
workstations handle this.  My app already has a user setup form that
defines the group a user belongs to as well as what permissions they
have to see data. Only administrators can add/delete users and set
permissions.   I could add a checkbox to that user setup form that sets
whether their copy of my app can update the server backend.  Only users
that are within the LAN of the SERVER would be allowed to do this; no
users connecting to the server via a WAN would be allowed to do this.
When these users close my app, the code would not only update their
local copy but also the server copy.  An alternative would be to create
a program that would schedule these updates.  I already have a small
program for scheduled backup and compaction of the server backend so I
could add updating of the server backend tables to this program.  It
has a timer (IT won't allow us to use Windows Scheduler). This might
actually be the best solution.  Users on the LAN where the server is
could just launch this small program, make sure the timer is set and
then leave their machines on over night.

Also "housekeeping" is an issue.  At what point do we start getting rid
of these temp updateDB files that are accumulating not only in the
server folder but also in the workstation folders. Since the frontend
will have a table that logs every temp updateDB filename and a date of
import, any entries where the date of import is over 60 days old, for
example, could be deleted from not only the table but also the folder.

These are just my initial thoughts and I would appreciate comments,
suggestions, etc.

Thanks.
David W. Fenton - 23 Dec 2006 21:53 GMT
> I'm concerned about the lack of support by Microsoft for database
> replication and indirect synchronization.  

MS has never been good supporting Jet replication. I can only think
that the reason they aren't trumpeting the existence of such an
incredibly well-implemented capability in one of their products is
because they think it will hurt sales of SQL Server. I think this is
the only reason they've never created a Jet server component (like
Filemaker has), because it would probably blow away SQL Server for
applications with up to 255 users.

I believe that the reason Jet replication is so good is because it
was the dry run for implementing the final SQL Server merge
replication. The development groups that worked on Jet 4 and
replication and on SQL Server overlapped a great deal, if I
understand correctly, and a lot of what went into Jet 4 replication
also went into SQL Server (and heterogeneous replication between Jet
and SQL Server).

> They don't even supply
> replication manager any more.

This is a vast stupidity, yes.

> In our tough IT environment, they don't
> allow unapproved third-party software (like TSI Synchronizer).
[quoted text clipped - 4 lines]
> Professional 2003, I seriously doubt they will allow its
> installation even if they can get a copy.

Show them the Jet Replication security update. Even if you were
doing direct replication, you'd need that, obviously, and that
includes the synchronizers. So, it *is* supplied direct from
Microsoft.

> Although Jet direct synchronization is a possibility, a number
> of users are on WANs so corruption is possible (many would say
> inevitable).

The loss of replicability is the main thing. That happens any time
you compact a replica with any of its "possible corruption" flags
set.

> The institution I work with is at the forefront of network
> security. Since my app is only used by a relatively small number
[quoted text clipped - 5 lines]
> differently than Jet and to not have any features regarding
> synchronization of table design or synchronization scheduling.

Sounds like a good plan at this point.

> So:
>
[quoted text clipped - 4 lines]
> will have its own "local" backend.  In addition, there will be a
> backend on a common server that all workstations can connect to.

I still don't understand why LAN-connected machines shouldn't all be
sharing a common back end stored on that LAN's server.

> 2) All back-end tables currently have a time-stamp field which is
> modified if data in the record is changed.
[quoted text clipped - 8 lines]
> empty of data.  The code then will write to the temp updateDB only
> the records that were added/modified/deleted.

You won't be able to user Autonumber PKs unless you make them part
of a compound key that identifies the source database.

>  The tables in the temp updateDB, however, will include an extra
>  field
[quoted text clipped - 13 lines]
> an area that needs more thought in order to keep it simple, but I
> think is definately doable.

I've done this already in a master/slave relationship. Here's what
you do:

1. use the PKs to determine new additions. That is, do an outer join
and find the records that don't exist on the other end.

2. use DAO to walk the recordset fields to find updated records.
This is a matter of writing on-the-fly SQL that is something like
this:

 UPDATE table1
 INNER JOIN table1 ON table1.PK = table2.PK
 SET table1.field = table2.field2
 WHERE table1.field <> table2.field2

Now, it's more complicated than that, as you have to account for
Nulls. And you can't use the same WHERE clause for all data types.
What I usually do is have a CASE SELECT on the field type that
writes an appropriate WHERE clause.

Then, to run it, you walk the fields collection of a recordset with
the updatable fields, and then write the SQL for each field. This
SQL is *very* efficient and runs very quickly, and it only updates
the fields that have been changed.

3. deletes are harder, because you can't delete a record that isn't
there. There are two approaches for this:

  A. use a DELETED flag and a DELETED field. The flag means "a user
  deleted this" and the DELETED field (a date/time field) says when
  it was deleted from the other replica. Now, the problem here is
  that you still need to get it around to all the other replicas,
  so your topology is going to matter a lot. You won't be able to
  synch with just any other database -- you'll only be able to have
  pairs of databases. I've never done this except in a master/slave
  relationship between two databases, so I didn't have to worry
  about that, and don't know what the ideal solution would be.

  B. do it like Jet replication and maintain a table of deletions.
  Then all you'd be doing is checking for additions to that table
  (just like in #1) and then processing the new records
  accordingly. This seems to me to be much easier than A) in a
  multi-database scenario like yours.

But all of these are dependent on generating unique PKs in each
database that are the permanent PKs.

> 4) My app will look to add records to the temp updateDB when the
> app closes (deletions would presumably already have been added).
> Once the temp updateDB is filled with the updated/modified/deleted
> data and the Yes/No flag set for each record, then the app will
> copy the temp updateDB from the local workstation to a common
> server location that all workstations can see.

Your temp database is just your method of sending the data, so I
guess that works. I've always had direct connections when I did this
or used text files (the first time I implemented this was between an
Access MDB and a MySQL database on a web server, the second was
between an Access MDB and another MDB downloaded from a web site;
notice a pattern? :).

> So, the idea is that the temp updateDB created by the app on local
> workstations should in the vast majority of cases be relatively
[quoted text clipped - 4 lines]
> rather than creating an entire temporary DB from code (besides
> I've got a lot of other coding to do!).

Tony Toews doesn't believe that to be true. He has code on his
website to create temp databases on the fly and he says it's just as
fast, because a Jet MDB created in code doensn't include anything
but the bare-bones properties needed to store the data.

I just don't want to spend the time writing and testing the code to
create the temp file! So I use the method you're proposing, but it's
not because of how long it takes to create the temp database from
scratch.

>  I already do it this way for my normal temporary database that my
>  app
> uses.  In two years, this techniques has always worked as intended
> and never failed.

It's exactly what I use for temp files.

> 5)  When my app starts up, it will look for temp updateDBs in a
> folder on the server.  If it finds any that have not already been
[quoted text clipped - 7 lines]
> that will log the filename of each temp updateDB that was imported
> as well as the import date.

I would suggest you'll want to archive both the temp databases and
the state of the real database before applying the changes from the
temp databases. This is so you can easily roll back to a previous
state if something goes wrong.

You'll want to do all your synching wrapped in transactions, too.

> So, I think this can work for updating workstations.  It's an
> indirect synchronization scheme because the temp updateDB and the
> local back-end file are only opened locally for updating of the
> local back-end file.

It's a direct analog of how Jet indirect synch works with dropbox
files.

> Scheduling is not needed.  

What about between the servers?

> When the app opens it will look for temp
> updateDB(s) to IMPORT and when it closes it will look to EXPORT
[quoted text clipped - 15 lines]
> violations, but it should ensure that the new backend file is
> updated.

Sounds like there are pitfalls there, but the basic outline sounds
about right.

> HOWEVER,  I still need to have a current copy of the backend on
> the server.  My app launcher allows users to download a copy of
[quoted text clipped - 15 lines]
> also the server copy.  An alternative would be to create a program
> that would schedule these updates.  

Yes, you'll have to schedule somehow. I know that it's possible to
launch an Access MDB that runs code automatically, and this works
when there's not a user logged on (I had a client whose daily
compacts were done this way).

> I already have a small
> program for scheduled backup and compaction of the server backend
> so I could add updating of the server backend tables to this
> program.  It has a timer (IT won't allow us to use Windows
> Scheduler).

a.sholes.

You could write a VBScript to do the same thing, you know, using
DAO. Would they let you schedule that?

> This might
> actually be the best solution.  Users on the LAN where the server
> is could just launch this small program, make sure the timer is
> set and then leave their machines on over night.

It could work, but I'd be worried about it -- it's too easy for it
to get shut down and not run.

> Also "housekeeping" is an issue.  At what point do we start
> getting rid of these temp updateDB files that are accumulating not
[quoted text clipped - 3 lines]
> import is over 60 days old, for example, could be deleted from not
> only the table but also the folder.

What about zipping them up? That gives you a lot more overhead?

Also, you might want to consider not updating every time, and just
updating when a certain number of changes have accumulated.

> These are just my initial thoughts and I would appreciate
> comments, suggestions, etc.

It's a *very* hard task, and you're running into some of the same
problems with IT, because you really need scheduling to remove as
much latency as possible. But I think it's doable.

But I don't envy you the task.

If I were in your shoes, I'd be putting my energy into lobbying
management into getting IT to approve use of indirect
synchronization.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan@hotmail.com - 23 Dec 2006 23:31 GMT
> > In our tough IT environment, they don't
> > allow unapproved third-party software (like TSI Synchronizer).
[quoted text clipped - 9 lines]
> includes the synchronizers. So, it *is* supplied direct from
> Microsoft.

Are you saying that the synchronizer needed for indirect
synchronization is already on the workstations??!  Direct
synchronization does work as I've already tested it and nothing was
added in the way of executables, dlls, etc.

> Sounds like a good plan at this point.

Glad that someone with your experience agrees.  I'd hate to put in the
time and then find I'd hit a dead end or had neglected to consider a
showstopper.

> I've done this already in a master/slave relationship. Here's what
> you do:
>
> 1. use the PKs to determine new additions. That is, do an outer join
> and find the records that don't exist on the other end.

I have very few tables that use autonumber PKs, but these could be a
problem.  I have one table where none of the human-enterable fields are
unique, therefore there has to be an autonumber PK.  But generally I
use compound keys.  I do not use Access to enforce referential
integrity and do it all in code (Okay, I got tired before deployment
and have about 5 out of 110 tables that do; but these are all small and
it would be relatively easy to do the referential integrity in code).

> 2. use DAO to walk the recordset fields to find updated records.
> This is a matter of writing on-the-fly SQL that is something like
[quoted text clipped - 14 lines]
> SQL is *very* efficient and runs very quickly, and it only updates
> the fields that have been changed.

I had forgotten that I've already coded this for about ten tables or
so.  When I first started playing around with replication I was
concerned about mass deletions bloating the replicated backend.  This
is because I have chosen to allow users to make backup copies of
certain tables directly in the backend that they are connected to.
That way if they screw up that table when editing/deleting, they can
quickly restore the original (i.e. they don't have to restore the
entire backend, just the table).  But when they make a new backup,
previously I was simply deleting all records in the backup table.
Since some of my tables have 100000+ records, this really bloats
replicated backends.  Therefore, I had already implemented the code you
suggest.  Of course, now I have to do it for the other 100 tables :).
The GUIDs caused me problems when implementing this code and I couldn't
just use Select *.   I had to explicitly write each field which was
ugly with one table that has 70+ fields (yes, these are virtually all
unique fields not contained in any other table).  But now without the
GUIDs this should be a whole lot easier.  Still it's a lot of work.

> 3. deletes are harder, because you can't delete a record that isn't
> there. There are two approaches for this:
[quoted text clipped - 14 lines]
>    accordingly. This seems to me to be much easier than A) in a
>    multi-database scenario like yours.

Yes, the deletes are a problem and I'll study your two suggestions
later.

> But all of these are dependent on generating unique PKs in each
> database that are the permanent PKs.

> Your temp database is just your method of sending the data, so I
> guess that works.
Yes.  I thought about a text file, but the coding will be substantially
tougher (at least for me), than if I just use an Access database. There
will be very, very few cases where that temp database will be greater
than 2MB; most of the time I'm sure it will be less than 1MB after
compacting (this is based on my experience with my current temp
database which has about 65 tables.

> > 5)  When my app starts up, it will look for temp updateDBs in a
> > folder on the server.  If it finds any that have not already been
[quoted text clipped - 14 lines]
>
> You'll want to do all your synching wrapped in transactions, too.

Good idea on the transaction wrappers.  I already have those in my app,
so I've done it before.  With regards to archiving, I think I'll do
this only for the server copy.  I'm going to beef up the small backup
and compact application (intially provided by Candace Tripp and
modified by me) to include handling the updating of the server backend
file.  For me that is the key backend file.  If any of the workstation
backend files blow up, it's not a real problem because the launcher is
already setup to allow users to download a fresh copy of the backend
file from the server if they need to.

> What about between the servers?
Yeah, this is where it gets trickier.

> > When the app opens it will look for temp
> > updateDB(s) to IMPORT and when it closes it will look to EXPORT
[quoted text clipped - 51 lines]
>
> a.sholes.
You got that right :)

> You could write a VBScript to do the same thing, you know, using
> DAO. Would they let you schedule that?
[quoted text clipped - 6 lines]
> It could work, but I'd be worried about it -- it's too easy for it
> to get shut down and not run.
Yeah, you're right.  Or users could forget to run it.  HOWEVER, it just
occurred to me that I've already got programming in my app to send
e-mails.  What if I just copy and paste this email code into the
backup/compact/update server backend app.  When the update of the
server backend file is complete, it will send an e-mail to the
appropriate individual(s).  E-mail received, everything went okay. No
e-mail, then there were most likely problems.  Of course the
backup/compact/update app can be operated manually, so that's what the
administrator would have to do if the timed update failed, for whatever
reason.

Fortunately, keeping the data up-to-date is not super critical most of
the time.  But there are certain times of the year where it is.

> > Also "housekeeping" is an issue.  At what point do we start
> > getting rid of these temp updateDB files that are accumulating not
[quoted text clipped - 3 lines]
> > import is over 60 days old, for example, could be deleted from not
> > only the table but also the folder.

If I'm going to use the compact/backup/update app for the server
backend, I should probably log which temp updateDB files were already
imported.  I don't think it is wise to store that in the
compact/backup/update front end.  Probably should store that
information in the backend file so that it will be universally
available to anyone who uses the compact/backup/update app (each
administrator will need their own copy).  Besides I might find it
useful in the future to have a history of updates files applied to the
server backend.  Since I plan to include the user's logon names in the
temp updateDB files, I can learn who is doing what and if they're doing
what they are supposed to with regards to maintaining data.

> What about zipping them up? That gives you a lot more overhead?
>
> Also, you might want to consider not updating every time, and just
> updating when a certain number of changes have accumulated.

Yeah, I could probably get away with that

> > These are just my initial thoughts and I would appreciate
> > comments, suggestions, etc.
[quoted text clipped - 4 lines]
>
> But I don't envy you the task.

Everything I've mentioned about IT is only getting worse.  Most of the
users are disillusioned and literally have to practice TM and chant to
keep from going crazy with the slowness of the network connections.
The response of anything dealing with the server in general is slow.
For example, at my location, it takes 7 minutes to copy a 50 MB file
from my hard drive to the server using Windows Explorer. At home with
cable modem, I can download 50 MB in just over a minute.  This by the
way is why I provide users with a local copy of the backend.  My app is
mostly about complicated analyses on tens to hundreds of thousands of
rows from multiple tables with multiple criteria provided by users.
Some take several minutes to run even with the backend file on the
local PC.  A comment I get from many users is that they like the speed
of the app (it's only because the backend is on the local PC).  They
are used to applications (MAXIMO, etc) where they have to wait and wait
and wait to accomplish their work.  I wanted my app to be used [since
it is really cool :)], so I decided I could forgo best practice and not
make everyone deal with an ultraslow backend on a LAN server (the fact
that we're not constantly updating data makes this feasible, IMHO).  Of
course, if they want to, users can connect to the backend file on the
server.  My launcher makes this very easy.  Just click a radio button
and the server location is input into a textbox and that's where my app
will go to find the backend file.  But no one does that, because they
prefer the speed.  However, that means that the responsibility is then
on me to  do what I can to make sure they have current data.

At this point it is really about me maintaining control.  I would hate
to implement Jet indirect synchronization and then have them yank the
synchronizer in a year or two causing my application to fail when it
comes to updating replicas.  No, this will be better.  Also, I don't
care for the bloat and size of replicated backends.  My backend went
from 130 MB to close to 300MB after it was replicated and after
multiple compactions.

One thing I didn't consider yet is conflicts.  I don't expect this to
be a huge problem because data entry personnel are only allowed to
update data for their site. There are four sites and while everyone can
see data from all four sites, they cannot update somebody else's site
data even though they may have the rights to update their own site
data.  This will assist in minimizing data conflicts.  Further, not
everyone can update data.  Only about 1 out of three users has been
given permissions to update data; the rest can only view and run
analyses.  But conflicts still can and probably will happen, and we
won't really know about them until probably well after the fact. Right
now it looks like the last temp updateDB file to update a backend is
the winner if there is a conflict.  If a new record's PK already
exists, then the fields will simply be updated instead of a new record
added.  If a row has already been deleted, well then I guess in that
case, the first deletion won.  I don't think it is going to be a show
stopper.  I'm finding that my users don't really want to update the
data, they just want to use the data for their analyses and view data
they need for their work.   Most want somebody else to do it.

> --
> David W. Fenton                  http://www.dfenton.com/
> usenet at dfenton dot com    http://www.dfenton.com/DFA/
rdemyan@hotmail.com - 24 Dec 2006 02:17 GMT
> > 2. use DAO to walk the recordset fields to find updated records.
> > This is a matter of writing on-the-fly SQL that is something like
[quoted text clipped - 4 lines]
> >   SET table1.field = table2.field2
> >   WHERE table1.field <> table2.field2

For importing from the temp updateDB, I know which databases to use to
get table1 (currently connected backend file) and table2 (temp
updateDB).

But for exporting, this is not clear.  The code will have created the
temp updateDB but it will be empty.  I think I'm going to need to make
a copy of the backend file when the app opens.  It will essentially be
a snapshot of the backend prior to any updates/edits/deletes.
Copying files on the local PC can be fast (75 MB on my machine takes
about 10 seconds). Then I need to do the join and update the temp
updateDB. I've never written code like that before but I guess if I
just change the Update statement above to:

UPDATE table3.....

table1 = local backend file table
table2 = copy of local backend file table created when app was first
opened during the session
table2 = temp updateDB table

There's probably more clever ways to do this, but this should work,
although speed is important.
rdemyan@hotmail.com - 24 Dec 2006 19:38 GMT
Well, I've been playing around with the SQL for synchronizing EXISTING
records in two tables. Eventually, I'm sure I can get it to work in a
TableDef loop, but I can see now that this is going to be really,
really slow (looping through over 100 tables).  And I'm just talking
about writing the updated and deleted records between my app and a temp
updateDB on the SAME workstation (no network connection).  The largest
table (130,000 rows) takes 40 seconds alone.  Add in 99 more tables
(okay they're smaller), but this could probably take at least 10
minutes.  And it doesn't really matter whether there have been any
changes to the tables or not. OTOH, Jet direct synchronization takes
only a few seconds when both databases are on the same machine.

A speedier although time-consuming-to-implement method is: when my code
makes an add/edit/delete to a table, at that point write the record to
the temp updateDB database. There would essentially be no effect on
speed in this case.  The hard part would be modifying all of the forms
where updates occur to also include writing to an external database.

I'm coming to the conclusion that there just aren't any good
alternatives given the existing IT environment that I have to work in.

Maybe what  I should do is stick with replication and direct
synchronization and implement as many processes as possible to protect
the backend replica on the server (the main backend).  Alert the user
when a database has lost it's replication (so they can create another
replica for local use).   Creating daily backups of the server replica
will probably be required.  If the server replica has a problem, then
it can be deleted, removed from the replica set and replaced by a
replica from the most recent "backup" replica (note: MakeReplica will
be used at all times, no file copying).

Hypothetical:  Hub backend replica in a star configuration is on the
server at SiteA (server replica).  User1 is at SiteB connected to SiteA
by a WAN.  User1 tries to direct synchronize his remote replica with
the server replica but an error message comes back saying that
synchronization cannot be accomplished because the server replica is
corrupted or no longer replicated (I hope there is such a message
available).

Can User1 at SiteB safely initiate the replication of a new server
replica at SiteA from an existing "backup" replica also located at
SiteA.  Or is this dangerous because of the WAN connection?

> > > 2. use DAO to walk the recordset fields to find updated records.
> > > This is a matter of writing on-the-fly SQL that is something like
[quoted text clipped - 27 lines]
> There's probably more clever ways to do this, but this should work,
> although speed is important.
David W. Fenton - 24 Dec 2006 22:01 GMT
> Well, I've been playing around with the SQL for synchronizing
> EXISTING records in two tables. Eventually, I'm sure I can get it
[quoted text clipped - 8 lines]
> not. OTOH, Jet direct synchronization takes only a few seconds
> when both databases are on the same machine.

Are you filtering your records based on the date stamps for updates?
You could do it one of two ways:

1. record the last synch date and filter on that.

2. process only the records where the date stamp doesn't match.

Processing the actual updates should be fast, as it's running a SQL
statement for each field.

I've done this on apps with 40 or so tables, and it was quite fast,
though the number of records was not as large as yours.

Are you sure you've code it right?

> A speedier although time-consuming-to-implement method is: when my
> code makes an add/edit/delete to a table, at that point write the
> record to the temp updateDB database. There would essentially be
> no effect on speed in this case.  The hard part would be modifying
> all of the forms where updates occur to also include writing to an
> external database.

Why duplicate data? What would you do when there are multiple
updates to a single record?

> I'm coming to the conclusion that there just aren't any good
> alternatives given the existing IT environment that I have to work
> in.

That's basically what I said on the front end.

Did you say earlier why Windows Terminal Server was out of the
question? That would give complete IT centralized control and
wouldn't require anything but Access and a little disk space for the
front ends for each user.

> Maybe what  I should do is stick with replication and direct
> synchronization and implement as many processes as possible to
> protect the backend replica on the server (the main backend).
> Alert the user when a database has lost it's replication (so they
> can create another replica for local use).  

You're missing a key point. It's much more likely to be the server
replica that loses replicability than the user replica, since the
user will be initiating the synch across the wire to the server, and
it's never the local replica that gets corrupted.

> Creating daily backups of the server replica
> will probably be required.  

Well, also, doing your remote synchs with a buffer replica that is
distinct from the actual main replica of record on the server would
help, too. That's basically the smallest possible replica farm (two
replicas).

> If the server replica has a problem, then
> it can be deleted, removed from the replica set and replaced by a
> replica from the most recent "backup" replica (note: MakeReplica
> will be used at all times, no file copying).

A replica farm would be preferable.

> Hypothetical:  Hub backend replica in a star configuration is on
> the server at SiteA (server replica).  User1 is at SiteB connected
[quoted text clipped - 3 lines]
> server replica is corrupted or no longer replicated (I hope there
> is such a message available).

I still don't understand why you've rejected the multiple star
topology, with hubs on each of the servers on each of the LANs, with
all users synching directly with the local server's hub replica
(perfectly safe on a local LAN), and then using indirect synch to
keep the server replicas synchronized with each other.

This would be a two-level star, with a central server at the hub of
a small star of servers, synching indirectly with the central
server, and then a secondary star with each server as the center,
with all synchs from the spokes to the center being direct.

> Can User1 at SiteB safely initiate the replication of a new server
> replica at SiteA from an existing "backup" replica also located at
> SiteA.  Or is this dangerous because of the WAN connection?

It's very dangerous, because the local CPU will be doing the
processing to create the new replica. It's more dangerous than any
other direct synch operation you've contemplated.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan@hotmail.com - 26 Dec 2006 22:42 GMT
Your suggestion about applying the filter put the spunk back in me :)

I'm making good progress.  I have the code setup to create and fill the
temp updateDB and it seems to be working well.  Took me quite some time
to make sure that all of my tables were set up correctly (no autonumber
PKs, all have TIME_STAMP field) and to make sure that all my code
always set the TIME_STAMP when a change is made to the data tables.

The 'checking for delete' process is what is taking the most execution
time.  With the TIME_STAMP filter for checking for additions and
updates, this part of the code takes as little as 2 seconds to execute
for 107 tables.

But the delete process can't use this technique because the record is
missing.  I'm creating a "snapshot" backend when the app opens (i.e.
just filecopy of the local backend).  Then when the app closes, the
delete process looks for non-matching records in the snapshot backend
versus the backend that the app is linked to.   This takes about 50
seconds at a minimum.  However, when I then deleted 130,000 rows from a
table it only took an extra 7 seconds.

What's taking so long is that I'm using generic code.  The tables are
looped through and then the index is looped through to get the PK.
This dynamic creation of the SQL is what appears to be taking the
lion's share of the time.  However, it beats the alternative of coding
for 107 tables and specifying each field name explicitly.  I don't even
know if that would save any execution time and I'm not inclined to find
out.

It would seem that the only way to trim this execution time down would
be to write the record(s) to the temp updateDB table when the record(s)
are actually deleted.  This might actually be a possibility, since with
virtually all table deletes, I use an action query like strSQLDELETE =
"DELETE *......".   Of course, I'm sure that there are probably a few
exceptions, but still I'd be able to find the vast majority very
quickly and then simply copy and paste the code and add a database
qualifier to the external table.  I'm going to look at this since then
I wouldn't have to check for deleted records upon creating the temp
updateDB which would save at least 50 seconds of time.

> > Well, I've been playing around with the SQL for synchronizing
> > EXISTING records in two tables. Eventually, I'm sure I can get it
[quoted text clipped - 101 lines]
> David W. Fenton                  http://www.dfenton.com/
> usenet at dfenton dot com    http://www.dfenton.com/DFA/
rdemyan - 27 Dec 2006 18:12 GMT
The code for importing of an update file is working and, as you noted
earlier, it is very fast (as expected).  There are still some
housekeeping issues I need to consider and I've started on them, but
I'll save the details for another post.

**Let me add a bit more about how I'm currently handling the deletion
of records issue.**

1) When the app opens on the remote PC it makes a "snapshot" copy of
the local backend file that the user is connected to.

2) When the app is closed, the code that EXPORTS changes to the temp
updateDB (the DB where updated records are stored and which will be
copied to the server) looks for records in the local backend file that
the user is connected to that are not contained in the  "snapshot"
copy.  If it finds any records these are written to the appropriate
table in the temp updateDB.  There is a flag in each table in the temp
updateDB that indicates to code that IMPORTS data from the temp
updateDB whether that data is to be added/edited (flag = false) or
deleted (flag = true).  For deletions this flag is set to true.

3) There are several issues with this method:

a) A copy of the backend file has to be created.  It is true that the
copy is made from a local PC file to another local PC file, so the
copying process is fairly fast.

b) In order to determine if there are record that were deleted, the
code has to check each table.  I don't see how a filter can be applied
in the current setup. That means that the process for over 100 tables
is relatively long.

*************How do I apply updates to the server
backend*****************************

This is the main last piece that I need to deal with.  If I understand
Jet indirect synchronization correctly, the synchronizer on the server
is running in the background looking for updates in the server dropbox.
When it finds one, it will process it.

Well in my homegrown synchronization scheme, I'll use the same method,
namely send the update file created by a remote user upon closing my
app to the server ImportDropBox.

But the real question is how do those updates in the ImportDropBox for
the server get applied.  There is no program running in the background
on the server like the Jet Synchronizer.  Window's scheduler is not
available to us to launch an Access program that could maybe handle
this (although I'm not completely sure how).  In one form or another,
this has to somehow be user initiated.

My current plan is:

1) Only users that are connected to the server via a LAN can initiate
the updating of the server backend (no WAN users).  If I keep the code
for this in my app, this is easy to do since I have a user setup screen
which administrators use to set permissions to view data.  This would
just be one more permission.

2) Should I use a hidden form in my app with a timer that monitors if
new update files have appeared in the server  ImportDropBox.  Then the
user could be alerted to apply the update or this could be done
automatically.  I could get away with this because in general we don't
update data very often.  The code would of course check if any of the
update files in the dropbox had already been imported (each import is
logged to a table).

A better alternative might be to launch another program at the same
time myapp is launched.  This second program would be the one
monitoring the server ImportDropBox and would automatically apply
updates.  But right now, I'm not sure if a timer on a form within in
application that does not have the focus, works within Access.  I need
to research this.

But either method requires that someone is logged in.  Even if I use a
separate app to accomplish the server synchronization, it is always
going to require that someone is logged into their computer at a
minimum.

But let's keep things in perspective and look at how we're doing it now
(BTW: the app is still in the deployment stage so we are still working
out coding and logistic bugs and adding customization; but I need to
figure this "synchronization" issue out and deploy it soon).  Updates
are sent to a central person (typically by spreadsheet or e-mail) and
that person has to manually update the server backend.  Then after the
server backend is updated an e-mail is sent out and remote users have
to download the updated backend from the server.

So from that perspective, this is a large improvement.  Further, since
there are multiple users using the app that are also connected to the
LAN where the server resides, each one would be given permissions  to
update the server backend so there would be some redundancy.  There
will still be latency, but it will be much shorter than it is now.  Of
course, Jet indirect would be better yet (but IT won't yet allow it).
OTOH, one of my other concerns is that MS will abandon Jet replication
as they currently seem to be on that path (I don't know how well it is
supported in A 2007).

Some thoughts, suggestion, before I embark on this coding effort.

Thanks.

> Your suggestion about applying the filter put the spunk back in me :)
>
[quoted text clipped - 141 lines]
> > David W. Fenton                  http://www.dfenton.com/
> > usenet at dfenton dot com    http://www.dfenton.com/DFA/
David W. Fenton - 27 Dec 2006 23:07 GMT
[]

> b) In order to determine if there are record that were deleted,
> the code has to check each table.  I don't see how a filter can be
> applied in the current setup. That means that the process for over
> 100 tables is relatively long.

If you used a DELETED flag and left the deleted records in the
tables and just hid them from users in forms and reports and your
processing routines, then you wouldn't have to anything at all,
since the UPDATE process would propagate the DELETED flags.

> *************How do I apply updates to the server
> backend*****************************

[]

> But the real question is how do those updates in the ImportDropBox
> for the server get applied.  There is no program running in the
> background on the server like the Jet Synchronizer.  Window's
> scheduler is not available to us to launch an Access program that
> could maybe handle this (although I'm not completely sure how).  

As I told you, you're just moving the problems around by hand-coding
this, as you still need capabilities that your brain-dead IT people
are prohibiting you from using. I really think you need to go to
management on this and explain that IT is being too restrictive and
it's costing extra money and effort in your work.

> In one form or another,
> this has to somehow be user initiated.
[quoted text clipped - 14 lines]
> check if any of the update files in the dropbox had already been
> imported (each import is logged to a table).

I think the way I'd do this in your situation is:

1. have LAN users run the update process on the server when they
upload their own updates to the server (assuming they are using the
same methods as the remote users, which may be wrong -- the LAN
users may be updating the server back end directly).

2. on a workstation that stays on all the time, run an Access app
that has one form with a timer that simply runs the update code on
the server at a specified interval. Or, that looks in the dropbox
every N minutes and processes any files there. Running it in a
Locked Workstation logon on a workstation removes the IT
restrictions. On WinXP, it could be just a "switch user" kind of
thing, and as long as the machine is left on, it will just run.

> A better alternative might be to launch another program at the
> same time myapp is launched.  This second program would be the one
> monitoring the server ImportDropBox and would automatically apply
> updates.  But right now, I'm not sure if a timer on a form within
> in application that does not have the focus, works within Access.
> I need to research this.

You wouldn't want but one instance of this update program, though.

> But either method requires that someone is logged in.  Even if I
> use a separate app to accomplish the server synchronization, it is
> always going to require that someone is logged into their computer
> at a minimum.

Yes. This is where the IT restrictions end up being just exactly as
restrictive for your hand-built synch as they are for Jet
replication. What i described above is *exactly* equivalent to
running your synchronizer on a workstation.

> But let's keep things in perspective and look at how we're doing
> it now (BTW: the app is still in the deployment stage so we are
[quoted text clipped - 5 lines]
> is updated an e-mail is sent out and remote users have to download
> the updated backend from the server.

Having a human adminstrator apply the updates via your Access code
should be a huge improvement, no? That way you won't have to worry
about all the problems that come with trying to do your updates
automatically.

> So from that perspective, this is a large improvement.  Further,
> since there are multiple users using the app that are also
[quoted text clipped - 5 lines]
> that MS will abandon Jet replication as they currently seem to be
> on that path (I don't know how well it is supported in A 2007).

The new ACCDB format does not support replication. Nor does it
support user-level security. However, you can still create MDB files
in A2K7 and replicate them and implement user-level security. So,
from my point of view, it's just about the same, except for the fact
that the new features of the ACCDB format are not available when you
use the "old" format.

I don't think that's much of an issue for replication, as you can
surely use an ACCDB for your front end and link to a replicated back
end. Thus you get most of the new features of the ACCDB (which are
almost all UI-related and not data-related, with the exception of
the multi-value fields, which I don't believe I'd use, in any case).

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan - 28 Dec 2006 00:42 GMT
> []
>
[quoted text clipped - 7 lines]
> processing routines, then you wouldn't have to anything at all,
> since the UPDATE process would propagate the DELETED flags.

I've created my own "sync" program (that I mentioned in the earlier
post) that launches with my app but is minimized with no focus. Its
purpose is to work in the background using a form timer to check for
update files in the Import DropBox for the server.   Preliminary tests
are showing good results (the code is very similar to the import code
used to update a remote backend).  But it's rudimentary and it's
thinking of all the ways people might "break" it, that is probably
where most of the effort is. You noted that I should only allow one
instance to run.  I'll have to research how to do that; I've never done
that before.

Also, I'm thinking that when my app gets closed, I won't include code
to close the "sync" app.  That way it'll be on the job longer so to
speak, probably until a user, on the LAN with the server, shuts down
their computer.

However, now I'm thinking about how to send out updates from the server
to the remote users.  I think the way I will do this is, when the
"sync' program detects update files in the server Import Drop Box, it
will run the code to apply those updates to the server backend. This
already appears to be working fine.

After the import, code will be run in the "sync" program to create an
export update file from the changes applied to the server backend.
This export update file will be stored in the Export DropBox on the
server (as opposed to Import DropBox).  This is the server folder where
my app used by remote users will look to find new update files and if
found, copy them to a local PC Import Drop box for processing to update
the local backend file. This too is working.

However, creating this export update file from the server backend file
is where it gets a bit tricky I think.  When this update file is
created from a LOCAL backend file, my app just logs the session start
for the app.  If when closing the app records are found that have a
Last_Updated date field that is greater than the session start
date/time, then these records are written to the temp updateDB that
will be sent to the server's Import Dropbox.

But for the server backend file this is different.  There isn't a
session start time.  I guess I'll have to use the last date that a temp
updateDB was created from the server backend as the comparison date.
This info can be stored in the backend file.

Again, though I'll have the same issue with deletes.  Your suggestion
about using a delete flag in the tables also occurred to me, but I
dismissed it quickly.  However, now that you raise it, I should give it
some consideration.  It would have the tremendous advantage of allowing
the code to quickly find the records that were "deleted" for inclusion
in the temp updateDB (essentially it would work the same as finding
updates).

The downside, of course, is the code changes that will be required to
my app.  Further I have to add a field to each table (actually I know
how to do this in code, so it's not really a big deal).  But not only
does the delete code in the app have to be modified, but all select
code does as well.  If it had been designed in at the beginning, not
much of a problem.  But at this stage, this seems like a tremendous
amount of work.

> Having a human adminstrator apply the updates via your Access code
> should be a huge improvement, no? That way you won't have to worry
> about all the problems that come with trying to do your updates
> automatically.

Huh?  You don't mean that what we're doing now with all of the manual
updating and sending out of emails is better than what I'm planning, do
you?

> The new ACCDB format does not support replication. Nor does it
> support user-level security. However, you can still create MDB files
> in A2K7 and replicate them and implement user-level security. So,
> from my point of view, it's just about the same, except for the fact
> that the new features of the ACCDB format are not available when you
> use the "old" format.

Leave it to Microsoft.  All the more reason I'm glad I'm making the
decision to handle my
own synchronization, despite the large effort required.

Hope you had a nice holiday!

> --
> David W. Fenton                  http://www.dfenton.com/
> usenet at dfenton dot com    http://www.dfenton.com/DFA/
David W. Fenton - 28 Dec 2006 02:22 GMT
>> []
>>
[quoted text clipped - 18 lines]
> noted that I should only allow one instance to run.  I'll have to
> research how to do that; I've never done that before.

Here's a suggestion:

Have a separate front end with linked tables to your shared
database. Have the synch program use that back end, instead of the
real shared back end. Then, before it runs, have it check to see who
is using that front end, and if anybody else is using it, prohibit
it from running. You might also do things like try to delete the LDB
file, which would mean that it's actually *not* in use. You can use
the ADO UserRoster for this, or the LDBUSR.DLL (look that up on
Microsoft's website, or on the Access Web, mvps.org/access).

> Also, I'm thinking that when my app gets closed, I won't include
> code to close the "sync" app.  That way it'll be on the job longer
> so to speak, probably until a user, on the LAN with the server,
> shuts down their computer.

I'd have an administrative user just leave their machine on
overnight, or when they are not in the office.

Or, if you implement what I described above, you could have an
administrator and a backup admin whose computers both launch the
synch app, but only one at a time would be able to do synchs. If you
had your synch app check for the other user each time it attempts to
synch, a second one could take over if the first one was shut down.

Ah, yes, just thought of a different method: store the synch app on
the server, and use *its* LDB file to figure out if it's already in
use. That makes more sense. And you could make sure it launches
exclusively.

> However, now I'm thinking about how to send out updates from the
> server to the remote users.  I think the way I will do this is,
> when the "sync' program detects update files in the server Import
> Drop Box, it will run the code to apply those updates to the
> server backend. This already appears to be working fine.

I assumed that's what you were doing already?

> After the import, code will be run in the "sync" program to create
> an export update file from the changes applied to the server
[quoted text clipped - 4 lines]
> box for processing to update the local backend file. This too is
> working.

I foresee an issue with this -- you'll never know for certain when
you can delete the files in the export dropbox.

> However, creating this export update file from the server backend
> file is where it gets a bit tricky I think.  When this update file
[quoted text clipped - 3 lines]
> start date/time, then these records are written to the temp
> updateDB that will be sent to the server's Import Dropbox.

Seems to me the import update process should create the export
database. That way, each import creates a new export. This would be
like a Jet replication generation number.

> But for the server backend file this is different.  There isn't a
> session start time.  I guess I'll have to use the last date that a
> temp updateDB was created from the server backend as the
> comparison date. This info can be stored in the backend file.

Ah, I see the issue. The problem is updates to the back end by LAN
users, which aren't done by imports. I'd use my synch program to run
an export on a schedule, then (basically the same way you'd do with
a replication hub and a production replica).

> Again, though I'll have the same issue with deletes.  Your
> suggestion about using a delete flag in the tables also occurred
[quoted text clipped - 3 lines]
> records that were "deleted" for inclusion in the temp updateDB
> (essentially it would work the same as finding updates).

Yes. There's be no separate process required for deletions.

Now, if you want to really clean them out, you can keep track of
which users have processed the deletes, and when everyone's gotten
word on the deletion of a particular record, you could delete it
from the master. Obviously, you'd delete the records from the user
database as soon as they've been exported to synch with the master.

Whoops, no, that won't work, since the next update from the master
would include the deletes, unless the central database kept track of
which users had already deleted it. I guess if you've stamped the
identity of the user who deleted the record, you'd know who was
already deleted it and could ignore deletions that come back to the
user who deleted it already.

But that's pretty complicated.

I'd just go with the delete flag and not worry about ever purging
the actual records.

> The downside, of course, is the code changes that will be required
> to my app.

Here's where a tool like Speed Ferret can come in handy. It allows
you to do global search and replaces, so you could write a query
that filters out the deletes in a table, and then search and replace
for every place where the table is used and replace it with the
query.

Another alternative is to rename the base linked table and replace
it with a query named the same as the original table link that
filters out the deletes. This would mean you don't have to edit
everything.

> Further I have to add a field to each table (actually I know
> how to do this in code, so it's not really a big deal).  But not
> only does the delete code in the app have to be modified, but all
> select code does as well.  If it had been designed in at the
> beginning, not much of a problem.  But at this stage, this seems
> like a tremendous amount of work.

Not with Speed Ferret. It costs about $100 and will pay for itself
the first time you use it. It's made by Black Moshannon Systems.

>> Having a human adminstrator apply the updates via your Access
>> code should be a huge improvement, no? That way you won't have to
[quoted text clipped - 4 lines]
> manual updating and sending out of emails is better than what I'm
> planning, do you?

No. All I'm saying is that automating the update part is an
improvement even if you do have to have a human being initiate some
parts of it on the central database.

>> The new ACCDB format does not support replication. Nor does it
>> support user-level security. However, you can still create MDB
[quoted text clipped - 6 lines]
> the decision to handle my
> own synchronization, despite the large effort required.

I don't see that there's really a problem, unless there's something
that breaks, as there are so many things. Here's a sobering
description of A2K7:

 http://allenbrowne.com/Access2007.html

Especially bad is the situation with installing multiple versions of
Access on Vista with A2K7. Apparently there's a solution, but it's
not clear if it will always work or not --it's still very early.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan - 28 Dec 2006 03:46 GMT
.

> Here's a suggestion:
>
[quoted text clipped - 6 lines]
> the ADO UserRoster for this, or the LDBUSR.DLL (look that up on
> Microsoft's website, or on the Access Web, mvps.org/access).

You totally lost me on this one.  By a separate front end, you don't
mean my main application, do you?  Are you suggesting that the sync
program ("front end"?) I wrote should link to the shared backend file
on the server?

> Ah, yes, just thought of a different method: store the synch app on
> the server, and use *its* LDB file to figure out if it's already in
> use. That makes more sense. And you could make sure it launches
> exclusively.

Store the sync app on the server....  hmmm......  So the idea is that
if it is on the server, I could set the option to have it open
exclusively.  Is your concern that if two users have the synch app open
on their machines and both synch apps try to process an update that
they find, that that will cause a problem.  Frankly, I hadn't
considered that!

> I foresee an issue with this -- you'll never know for certain when
> you can delete the files in the export dropbox.

Yes, clean up of these temp updateDBs will be an issue.  How does Jet
indirect synchronization handle this?  It's sending message files,
isn't it?  Couldn't I just copy the technique used there?

> Seems to me the import update process should create the export
> database. That way, each import creates a new export. This would be
> like a Jet replication generation number.

Yes this is my current plan.  When the sync app processes an import to
the shared backend, it then creates a temp updateDB based on the shared
backend for EXPORT to the remote users.

> Ah, I see the issue. The problem is updates to the back end by LAN
> users, which aren't done by imports. I'd use my synch program to run
> an export on a schedule, then (basically the same way you'd do with
> a replication hub and a production replica).

Actually updates by LAN users is an issue that occurred to me after I
posted previously, and I've been wondering how to handle that.
Scheduling has its advantages, but it could also mean a lot of updating
of remote user apps that are unnecessary.  On the other hand, if there
is virtually no data in the temp updateDB sent to the remote users,
then that import will only take a few seconds, which would not be
noticeable since my app takes about 15 to 20 seconds to load anyway.
But the file copy from the server to the remote machine will probably
take longer.  Originally I had thought that the model for the temp
updateDB would be relatively small.  It's actually 2.5 MB when it is
completely empty of data.  So, it's at least 2.5 times larger than I
thought.  Copying it to remote machines over a WAN might take a little
time (but is probably not worth worrying about).

> Yes. There's be no separate process required for deletions.
>
[quoted text clipped - 16 lines]
> filters out the deletes. This would mean you don't have to edit
> everything.

Yes, I thought of this too because I already have RWOP queries. At
first, I thought I could just modify the RWOP queries, but this won't
work because the users can't get at the tables directly with the
security.  The only way they can get at the DeleteFlag is through the
RWOP queries. So, I'd have to rename the existing RWOP queries with a
prefix and then create new queries with the old names of the RWOP
queries and adding "WHERE DeleteFlag = False).  This will add another
layer of queries (pushing it up to about 300 total in the main app).

If I implement the delete flag system, I will do this though, as there
is no way I can go through all of my code to find all of the select
statements, etc and modify them.  Speed Ferret could help with this as
well??

> > Further I have to add a field to each table (actually I know
> > how to do this in code, so it's not really a big deal).  But not
[quoted text clipped - 5 lines]
> Not with Speed Ferret. It costs about $100 and will pay for itself
> the first time you use it. It's made by Black Moshannon Systems.

I'm going to buy this anyway.  I've heard good things about it.
rdemyan - 28 Dec 2006 06:57 GMT
I looked at Speed Ferret.  The cost is actually just under $200.
Further, it won't work unless Access 2002 is also installed.  My
machine currently only has A2003 on it.  I looked on their website and
there is mention of a forthcoming upgrade that will work with A2003.
However, the date of that notice is 2004???!  Since nothing has
happened yet, it makes one wonder if they are still interested in
improving the product.

An alternative is Find and Replace from Rick Fisher Consulting.  Cost
is $37 and I've been playing around with the demo copy.  It could do
the trick although I wish it had the capability to Find and Replace on
one string subject to another string or something similar.

I've ordered F&R.   I'm gonna bite the bullet and implement setting the
IsDeletedFlag method for "deleting" records in tables. It'll probably
require a full day.  I only wish I didn't have to add another 110
queries, but that's the way it is.

In the end, I'll probably be happy that I spent the time to do this.
It'll certainly make my "homegrown synchronization" a lot simpler.

> .
> >
[quoted text clipped - 106 lines]
> >
> I'm going to buy this anyway.  I've heard good things about it.
rdemyan - 28 Dec 2006 18:51 GMT
Well, the switchover to a different table record delete logic is not
going as well as I had hoped (from a time perspective).  It took longer
to set up the tables and queries than I thought because I also took the
opportunity to clean up a lot of objects that were no longer being
used.   Okay, that's fine.

However, it is the code that is the major problem.  Almost all of my
add/edit/deletes are done in code.  Most of the add/edits using DAO
recordsets and deletes using Action queries.

What I'm just now realizing is that this is a more complex process than
I realized.  Why?  Because you have to take into account that when a
record gets deleted, a user may try to recreate it.  They don't know
that the record really wasn't deleted but instead that the
IsDeletedFlag was set to true.  So they try to recreate the record and
get a duplicate primary key error unless my code accounts for that
(which it currently does not).

Maybe I should have coded the additions/edits differently but it's too
late now.   It's also too late to abandon implementing the new delete
logic.

I'm posting this because if anyone is following along on this thread,
this is something you need to consider for your situation.
David W. Fenton - 29 Dec 2006 00:39 GMT
> However, it is the code that is the major problem.  Almost all of
> my add/edit/deletes are done in code.  Most of the add/edits using
> DAO recordsets and deletes using Action queries.

Doesn't F&$ search code? Speed Ferret does.

> What I'm just now realizing is that this is a more complex process
> than I realized.  Why?  Because you have to take into account that
[quoted text clipped - 3 lines]
> record and get a duplicate primary key error unless my code
> accounts for that (which it currently does not).

Bummer. Why are you re-using PKs? I'd never do that, myself.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan - 28 Dec 2006 19:23 GMT
Well, the switchover to a different table record delete logic is not
going as well as I had hoped (from a time perspective).  It took longer
to set up the tables and queries than I thought because I also took the
opportunity to clean up a lot of objects that were no longer being
used.   Okay, that's fine.

However, it is the code that is the major problem.  Almost all of my
add/edit/deletes are done in code.  Most of the add/edits using DAO
recordsets and deletes using Action queries.

What I'm just now realizing is that this is a more complex process than
I realized.  Why?  Because you have to take into account that when a
record gets deleted, a user may try to recreate it.  They don't know
that the record really wasn't deleted but instead that the
IsDeletedFlag was set to true.  So they try to recreate the record and
get a duplicate primary key error unless my code accounts for that
(which it currently does not).

Maybe I should have coded the additions/edits differently but it's too
late now.   It's also too late to abandon implementing the new delete
logic.

I'm posting this because if anyone is following along on this thread,
this is something you need to consider for your situation.
rdemyan - 28 Dec 2006 19:29 GMT
The continuing saga:

After trying to fix the code to use an IsdeletedFlag instead of
actually deleting records, I've abandoned the effort. I'm reverting to
last night's backups.  The changes required are much, much larger than
I ever imagined.  I'm now concerned that I will have to go back and
retest all that functionality that took weeks to test in the past and
currently works.

So, I'll just have to contine with my homegrown sync plan dealing with
deleted records in a different way.

> Well, the switchover to a different table record delete logic is not
> going as well as I had hoped (from a time perspective).  It took longer
[quoted text clipped - 20 lines]
> I'm posting this because if anyone is following along on this thread,
> this is something you need to consider for your situation.
David W. Fenton - 29 Dec 2006 00:42 GMT
> So, I'll just have to contine with my homegrown sync plan dealing
> with deleted records in a different way.

I would say that the best way would be to copy Jet replication's
method. Create a tombstones table that has a field that says which
table a record was deleted from, the date of deletion and the PK.
Then you'll join each table to the PK field of the tombstones table,
filter on the table name (and, perhaps, the deletion date) and then
you'll know which records to delete.

However, you're going to run into *exactly* the same problem as with
the delete flag because you're re-using PKs. I can't think of any
method for deleting records where that will not be a problem.

I think you have to re-engineer your app to never re-use PKs. A
random autonumber is a very good candidate for that.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

David W. Fenton - 29 Dec 2006 00:37 GMT
> I looked at Speed Ferret.  The cost is actually just under $200.

Huh. It's been $99 for years and years.

It would have still been worth it the first time I bought it and
used it -- it would have paid for itself in saved time on the first
use.

> Further, it won't work unless Access 2002 is also installed.  My
> machine currently only has A2003 on it.  I looked on their website
> and there is mention of a forthcoming upgrade that will work with
> A2003. However, the date of that notice is 2004???!  Since nothing
> has happened yet, it makes one wonder if they are still interested
> in improving the product.

That's a concern. I'd write to them.

Do you have A2K? I do all my programming in A2K for distribution on
later versions of Access, so I'd have no issues with it myself.

> An alternative is Find and Replace from Rick Fisher Consulting.
> Cost is $37 and I've been playing around with the demo copy.

I've heard good things about it, but it's not as versatile and
feature-rich as Speed Ferret.

> It could do
> the trick although I wish it had the capability to Find and
[quoted text clipped - 9 lines]
> this. It'll certainly make my "homegrown synchronization" a lot
> simpler.

I think so, too. All your alternatives would have taken a lot longer
to implement and would be subject to a much larger degree of error
in the implementation. This is a simple change that is just
time-consuming.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

David W. Fenton - 29 Dec 2006 00:32 GMT
>> Here's a suggestion:
>>
[quoted text clipped - 12 lines]
> the sync program ("front end"?) I wrote should link to the shared
> backend file on the server?

Know. I'm suggesting that instead of using the back end directly,
your synch program could use an intermediary MDB file with linked
tables and the only purpose of this intermediary MDB file would be
to provide you with an LDB file that would allow you to restrict to
one user (something you can't do when using the real back end).

>> Ah, yes, just thought of a different method: store the synch app
>> on the server, and use *its* LDB file to figure out if it's
[quoted text clipped - 4 lines]
> that if it is on the server, I could set the option to have it
> open exclusively.

No. That's not an MDB option, but a program option. And I wouldn't
open it exclusively, because then you lose the possibility of having
two users running it as backup. Instead, I'd allow it to be opened
by more than one user, but only one user at a time allowed to
actually run the synchs. This would mean you'd have to have your
synch timer run code to check how many users have it open. If User1
opens it, he'll be running the synchs. If User2 then opens it, her
copy will then wait until User1 logs out before it will ever run any
synchs.

> Is your concern that if two users have the synch app open
> on their machines and both synch apps try to process an update
> that they find, that that will cause a problem.  Frankly, I hadn't
> considered that!

Yes, of course it will cause problems. From my point of view you
want:

1. only one user at a time running the synchs.

2. guarantee that the synchs will run.

To get #2, you probably need more than one user running the synch
program at a time, so that if somebody shuts down her PC, there will
still be a copy of the synch program running. So, to get #2, you
have to have #1 written in a way that it checks constantly if it's
the only user and only starts running the synchs when it is.

>> I foresee an issue with this -- you'll never know for certain
>> when you can delete the files in the export dropbox.
>
> Yes, clean up of these temp updateDBs will be an issue.  How does
> Jet indirect synchronization handle this?  It's sending message
> files, isn't it?  Couldn't I just copy the technique used there?

It's sending a different set of message files for every replica
pair. My understanding was that you'd be creating a single file for
each "generation" and multiple remote databases would process it. If
you do it like Jet, then you could safely have the remote database
delete the message file as soon as it successfully closes the
transaction in which the synch operations were performed.

>> Seems to me the import update process should create the export
>> database. That way, each import creates a new export. This would
[quoted text clipped - 3 lines]
> import to the shared backend, it then creates a temp updateDB
> based on the shared backend for EXPORT to the remote users.

If you do it this way, rather than creating a message database for
every remote database, you'll have to keep track of which remote
databases have processed the message database. When all of them have
done so, you can archive it.

>> Ah, I see the issue. The problem is updates to the back end by
>> LAN users, which aren't done by imports. I'd use my synch program
[quoted text clipped - 15 lines]
> remote machines over a WAN might take a little time (but is
> probably not worth worrying about).

That's where zipping comes in. If you zip it up, it's like to be 1/4
the size or less. That can very easily be automated.

>> Yes. There's be no separate process required for deletions.
>>
[quoted text clipped - 31 lines]
> select statements, etc and modify them.  Speed Ferret could help
> with this as well??

Absolutely. It's a search and replace program. I'm afraid you'd
still have to run the search/replace for each table, though.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan - 29 Dec 2006 15:55 GMT
> That's where zipping comes in. If you zip it up, it's like to be 1/4
> the size or less. That can very easily be automated.

Never occurred to me to do this.  I checked and if I zip this 2.5 to 3
MB file in Windows Explorer it goes down to about 120 KB!  That's
excellent, and I will implement it if I can (IT may be the issue).

Currently, IT has code that executes to remove any zip files attached
to e-mails received by users.  Since everyone is still using Windows
2000, I don't believe that there is a Zip program like with Windows XP.
But I think everybody does have WinZip.  Although I've never used it
in that IT environment I do know that people trick the e-mail stripper
by simply replacing .zip with something else.  Then when a user
receives that file they rename it (back to .zip) and unzip it.  That
means that they must have some type of utility program installed
(probably WinZip).

I'll be searching for code to do this.  I heard rumors that IT will be
rolling out XP professional in the near future.  I would imagine that
they will use the native XP zip program and abandon the 3rd party
program currently in use.  So, I'll need to include code that looks for
both programs: if it can't find one, it looks for the other.

Thanks!
David W. Fenton - 29 Dec 2006 20:11 GMT
> I'll be searching for code to do this.

Check out http://www.frez.co.uk/freecode.htm, which refers to the
ZLib DLL that you can download for free and distribute with your app
for free.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan - 29 Dec 2006 20:09 GMT
> >> Here's a suggestion:
> >>
[quoted text clipped - 18 lines]
> to provide you with an LDB file that would allow you to restrict to
> one user (something you can't do when using the real back end).

I'm not clear on why I wouldn't have the synch program link to the
shared backend. I currently use the Jet UserRoster in my main app.  So
I could use it there. I believe I am correct in saying that a .ldb file
is not created for the backend until someone actually uses data from
the backend.  So an app just sitting there linked to the backend
doesn't cause a .ldb file to be created for the backend.

So if I link the synch program directly to the backend and am careful
in my code to make sure that all recordsets are closed and set to
Nothing, then why wouldn't this work.  Before the code applies any
updates, it would first check to see via the Jet UserRoster if anyone
else is "logged in" to the backend.  If so, update will not be applied.
When the other user finishes, then the update is applied.

After an update is applied, a record is written to a table in the
backend with the details of that update file.  When the synch program
timer fires it checks what files are in the dropbox and which files
have been imported.  If already imported, the import code is not run.

This seems like it should work.
David W. Fenton - 29 Dec 2006 21:36 GMT
>> >> Here's a suggestion:
>> >>
[quoted text clipped - 22 lines]
> I'm not clear on why I wouldn't have the synch program link to the
> shared backend.

Because that provides you a way to monitor and limit the number of
users running the synch program. If you link directly to the back
end, you can't distinguish your regular users from the user of the
synch app.

> I currently use the Jet UserRoster in my main app.  So
> I could use it there. I believe I am correct in saying that a .ldb
> file is not created for the backend until someone actually uses
> data from the backend.

Not entirely. A back end file will not have an LDB just because
someone opens a front end with tables linked to it. But all it takes
is the initialization of a DAO database variable to create the LDB
file. Opening a form bound to a table will also create the LDB.

The point is that if you open an MDB, the LDB is created
immediately. Opening a front end creates an LDB for the front end,
but not for the back end. So, you could either use the intermediary
front end or run the synch MDB in shared mode (I'm not certain if
this would be risky or not).

> So an app just sitting there linked to the backend
> doesn't cause a .ldb file to be created for the backend.

We're not talking about the back end's LDB, but any LDB that could
be used to keep track of who is logged on. It could be the LDB for
your synch app or the LDB for the intermediary front end I suggested
(which is not really a front end -- it's just a shell MDB with table
links in it, used for no purpose except to track who has it open).

> So if I link the synch program directly to the backend and am
> careful in my code to make sure that all recordsets are closed and
[quoted text clipped - 3 lines]
> update will not be applied.
>  When the other user finishes, then the update is applied.

But how can you then apply synchs when the regular users are
editing? Or is that what you want to avoid? I thought we were
talking about how to prevent multiple users from running the synch
program simultaneously.

> After an update is applied, a record is written to a table in the
> backend with the details of that update file.  When the synch
[quoted text clipped - 3 lines]
>
> This seems like it should work.

I don't think we're any longer talking about the same thing at all.

Signature

David W. Fenton                  http://www.dfenton.com/
usenet at dfenton dot com    http://www.dfenton.com/DFA/

rdemyan - 29 Dec 2006 22:09 GMT
No, it looks like we're not talking about the same thing.

Sorry to be so dense.  But I'm still not following you.  Let me state a
few things first to see if we are on the same page.

1) Updates will be applied to the shared production backend directly,
or should it be a copy?
2) If the production backend, then the concern is that the code in the
synch app may be updating a record in a production backend table that a
user connected to the production backend is trying to update at the
same time.
3) Synch app is on each each user's local PC?
4) Intermediary app is where?  On each user's local PC as well or in
the folder on the server where the shared production backend (or copy)
is.  If we're trying to restrict the intermediary app to one user, then
wouldn't it have to be on the server.  There's no way to control this
if a copy of the intermediary app exists on each user's intermediary
machine.
5) My plan was to launch the synch app when my main app launches; but
only for users who are given permission to do so (this would be set in
my main app when a user is setup to use the main app).  The reason for
this is I want to restrict who can launch a synch app to only those
individuals who are connected to the LAN where the shared production
backend is.  Sure I could not distribute the Synch app to the WAN
users, but my plan seems safer since users could still get a copy off
the server.  This way I don't need to add additional instructions for
new users (the more instructions, the more confusion, the more
calls......).

I guess I just don't see how having the intermediary app tells me
anything about users who are logged in directly to the production
backend.  I thought that the way to determine this was to see if there
is a .ldb file for the backend file and/or use the Jet User Roster.

Sorry, but I'm just not following you so I'll stop my post here rather
than ramble on with a false set of assumptions or understanding.

> >> >> Here's a suggestion:
> >> >>
[quoted text clipped - 79 lines]
> David W. Fenton                  http://www.dfenton.com/
> usenet at dfenton dot com    http://www.dfenton.com/DFA/
David W. Fenton - 30 Dec 2006 19:31 GMT
> No, it looks like we're not talking about the same thing.
>
[quoted text clipped - 3 lines]
> 1) Updates will be applied to the shared production backend
> directly, or should it be a copy?

You have to apply them at some point, so, yes, you have to apply
them to the production back end. Moving it to a copy just adds
latency to the time it takes for updates to move in both directions,
from the LAN users to the remote users.

> 2) If the production backend, then the concern is that the code in
> the synch app may be updating a record in a production backend
> table that a user connected to the production backend is trying to
> update at the same time.

Yes, but that should be blocked, and all you need is code that
handles that kind of error.

> 3) Synch app is on each each user's local PC?

Er, I don't think so, unless you're now defining the synch app
differently than I understood you to be defining it a few days ago.
I thought the synch app was designed to do the scheduled synchs,
looking in the inbox on the server and processing the files dropped
there by the remote users (and, I presume, doing the same thing in
the outbound direction).

Now, each remote user has to have functionality to process it's
updates (both incoming and outgoing), so I guess that's the same
application, yes.

But one copy of that app, it seems to me, has to be differently
designed than the others, and that's the one that is managing the
production database.

> 4) Intermediary app is where?

That whole issue only applies to the one copy of the synch app that
is handling the production database. The whole point is that your
synch app is working like the Jet replication synchronizer -- it
"manages" a database, and that database is the production database,
so it has to work somewhat differently than the synch apps managing
the other databses, which are not multi-user.

> On each user's local PC as well or in
> the folder on the server where the shared production backend (or
> copy) is.  If we're trying to restrict the intermediary app to one
> user, then wouldn't it have to be on the server.  There's no way
> to control this if a copy of the intermediary app exists on each
> user's intermediary machine.

First off, a remote user's synch app will be picking up an update
MDB from the dropbox on the server, and writing its own update MDB
to the inbox on the server. It will be operating only on the local
laptop's or workstation's database -- it won't be opening the
production database across the WAN or you'll be right back where you
started.

Now, perhaps you're just going to be copying the whole production
database off to the remote user? That would obviate the need for any
outgoing message databases, and sounds like a good thing to me,
because then you could compare the local database to the one from
the server. All you'd do would be zip up the production replica and
copy the zip file.

Now, that's subject to the remote possibility of corruption, as you
could be zipping up a file that's in an incomplete state (say some
data pages have been written from a transaction but not all at the
moment that your zip library compresses the file sectors), but in
all the years I've copied open MDB files, I've never seen a single
corruption. Of course, you'd be doing it systematically, so perhaps
it *would* be better to have an intermediary database that is
populated on a schedule (once every 10 minutes?) with data from the
production database. When it's created, then you'd zip it up, and
whatever the last zip copy is is what the remote users would
download. That seems like a better idea to me.

> 5) My plan was to launch the synch app when my main app launches;
> but only for users who are given permission to do so (this would
[quoted text clipped - 6 lines]
> add additional instructions for new users (the more instructions,
> the more confusion, the more calls......).

OK, well, I'm confused again. Why would you want multiple LAN users
synching? Don't you want your LAN users using the productiong
database?

> I guess I just don't see how having the intermediary app tells me
> anything about users who are logged in directly to the production
> backend.

It doesn't, and I never suggested it did. I thought you were
designing a scenario where you had a single instance of a synch app
that was running to manage the synchs for your production database,
and, like the synchronizer, you want only one process doing that
synchronizing.

One way to limit that to one user is the intermediary MDB that I
suggested.

> I thought that the way to determine this was to see if there
> is a .ldb file for the backend file and/or use the Jet User
> Roster.

If you're trying to limit the synching to one user at a time, that
won't accomplish anything, because you won't be able to distinguish
editing users from the synch user.

> Sorry, but I'm just not following you so I'll stop my post here
> rather than ramble on with a false set of assumptions or
> understanding.

I find your description above confusing and contradictory. I'm
obviously missing something major.

It seems to me that you're replicating indirect replication.

You'll have a synch app that replaces the synchronizer on each
machine. You'll want only one copy at a time running.

Your dropboxes will use zipped-up MDB files to push updates back and
forth. It seems to me that your outgoing message MDB from the
production server will need to be a full copy of the production
database, but the ones coming in from the remote users need only be
the records that have been added/updated/deleted in comparison to
the full copy of the prudction database.

But the key point is: you don't want multiple users running a synch
at once on the production database, because that will increase the
chances of update collisions. You'll want one synch app running on
your LAN, taking care of scanning the incoming dropbox for update
databases and then applying those updates to the production
database.

There are two goals I see there:

1. you want only one copy of the synch app, BUT

2. you don't want to lose the synching when some user happens to
shut down her workstation (and close the single running synch app).

That is accomplished by one of the two methods I described, either
sharing the synch app and using its LDB file to figure out who got
to it first, or using the intermediary MDB I suggested for the sole
purpose of tracking who is running the synch program. The first user
in the LDB gets the privilege of running the synchs. Users who start
up the synch program when there's already someone in the LDB won't
get to synch. But if you have, say, a 10-minute timer-based synch
interval, you'd want to have it check the LDB file each 10-minute
period to see if all the users that were logged on when the synch
app was launched have now logged out. That