473,387 Members | 1,497 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Very Strange MySQL Problem

I have RH9 and am using the PHP and MySQL that came with it. I was
doing fine with all manner of my web pages for this app until I
started having this very strange problem.

It's a work order mgmt system. I have 3 tables for the orders:
TicketsOpen, TicketsVoided, and TicketsResolved. When one wants to
void a ticket, they click it, choose Void, and it is copied to the
TicketsVoided table, then removed from the TicketsOpen. And I can
imagine you can apply that same concept to the TicketsResolved table.
Likewise, I have a process where one can unvoid and reopen (unresolve)
a ticket, copying back to TicketsOpen, and deleting from the previous
table.

(What I'm about to mention below will take some brain cells. Use em if
you got em. Otherwise, skip this posting.)

The Void.php web page will SELECT the ticket from TicketsOpen. I use 2
IDs for a ticket. One is the identity column: ID. The other is a
TRACKING_NUMBER (TN for short), which is a random, non-unique
alphanumeric string that is used for informing the customer. It's
simply based on month, day, a random number in the thousands, and then
is Base36 encoded to shorten it up. The Void.php page is told what the
ID is, so it reads the record into variables. Then, it changes the TN
variable to (TN . ' capture') -- we'll call that TN2 for short. Then,
it does an INSERT to write this record data to TicketsVoided. Then, it
does a SELECT on TN from TicketsVoided for the TN2 value. When it
finds it, it gets the new ID. (ID2, let's call it.) Then, it does an
UPDATE where record ID is ID2, setting the TN (that had a value of
TN2) back to TN w/o ' capture'. Then, it does a DELETE on TicketsOpen
by the original ID. It concludes by providing a link to go back to the
ticket queue for TicketsOpen. Again, the pseudocode is:

id1 = initial ticket ID of TicketsOpen that we want to void
orec = select our void request on id = ID1 from TicketsOpen
tn1 = orec.tn
tn2 = orec.tn + ' capture'
orec.tn = tn2
insert orec into TicketsVoided
vrec = select from TicketsVoided where tn = tn2
id2 = vrec.id
vrec.tn = tn1
update TicketsVoided with vrec where tn = tn2
delete from TicketsOpen where id = id1

Well, this runs just fine if I go slow. But if I start firing void.php
faster with my mouse, mostly likely by the fourth or fifth time it
will run without an error message, but return an incorrect result in a
variety of ways. Sometimes it never deletes the original record from
TicketsOpen. Sometimes it creates two records in TicketsVoided but
with only one of them having the TN set to TN2, while the other is set
to TN1.

And I bet you anything that if I move the whole project to PostgreSQL,
this problem won't occur.
Jul 17 '05 #1
4 2809

On 9-Nov-2003, go********@hotpop.com (Google Mike) wrote:

I have RH9 and am using the PHP and MySQL that came with it. I was
doing fine with all manner of my web pages for this app until I
started having this very strange problem.

It's a work order mgmt system. I have 3 tables for the orders:
TicketsOpen, TicketsVoided, and TicketsResolved. When one wants to
void a ticket, they click it, choose Void, and it is copied to the
TicketsVoided table, then removed from the TicketsOpen. And I can
imagine you can apply that same concept to the TicketsResolved table.
Likewise, I have a process where one can unvoid and reopen (unresolve)
a ticket, copying back to TicketsOpen, and deleting from the previous
table.

(What I'm about to mention below will take some brain cells. Use em if
you got em. Otherwise, skip this posting.)

The Void.php web page will SELECT the ticket from TicketsOpen. I use 2
IDs for a ticket. One is the identity column: ID. The other is a
TRACKING_NUMBER (TN for short), which is a random, non-unique
alphanumeric string that is used for informing the customer. It's
simply based on month, day, a random number in the thousands, and then
is Base36 encoded to shorten it up. The Void.php page is told what the
ID is, so it reads the record into variables. Then, it changes the TN
variable to (TN . ' capture') -- we'll call that TN2 for short. Then,
it does an INSERT to write this record data to TicketsVoided. Then, it
does a SELECT on TN from TicketsVoided for the TN2 value. When it
finds it, it gets the new ID. (ID2, let's call it.) Then, it does an
UPDATE where record ID is ID2, setting the TN (that had a value of
TN2) back to TN w/o ' capture'. Then, it does a DELETE on TicketsOpen
by the original ID. It concludes by providing a link to go back to the
ticket queue for TicketsOpen. Again, the pseudocode is:

id1 = initial ticket ID of TicketsOpen that we want to void
orec = select our void request on id = ID1 from TicketsOpen
tn1 = orec.tn
tn2 = orec.tn + ' capture'
orec.tn = tn2
insert orec into TicketsVoided
vrec = select from TicketsVoided where tn = tn2
id2 = vrec.id
vrec.tn = tn1
update TicketsVoided with vrec where tn = tn2
delete from TicketsOpen where id = id1 Well, this runs just fine if I go slow. But if I start firing void.php
faster with my mouse, mostly likely by the fourth or fifth time it
will run without an error message, but return an incorrect result in a
variety of ways. Sometimes it never deletes the original record from
TicketsOpen. Sometimes it creates two records in TicketsVoided but
with only one of them having the TN set to TN2, while the other is set
to TN1.

And I bet you anything that if I move the whole project to PostgreSQL,
this problem won't occur.


Just a thought, but are you using ignore_user_abort() to prevent the script
from terminating mid transaction?

Why are you using TN instead of id in your where clauses? If TN is not
unique, you are risking some very weird db problems down the road. Why not
use the unique record id instead of the random number in composing the TN so
it will be unique or don't use TN as the key in your select/insert/updates.

You might consider one table for tickets and marking them with a status or
having a status table which could prevent the problem caused by having to
move records between tables.

--
Tom Thackrey
www.creative-light.com
tom (at) creative (dash) light (dot) com
do NOT send email to ja*********@willglen.net (it's reserved for spammers)
Jul 17 '05 #2
Thanks to the programmers of PHP, they made the SharedMemory API. That
solved the problem. You see, it appears that the API in PHP that
writes to MySQL works somewhat asynchronously sometimes, especially
when forced by someone hitting pages very rapidly. If one wants to do
an insert, followed by an update, they may end up with two different
records in some cases where they don't use the unique ID column, but
some other column or set of columns. The resolution I used was to make
one single insert with everything, including the unique ID, but the
trick question I had for awhile was, "How do I ensure that I'm going
to be the one holding the record with the unique ID, and not some
other process take that ID?"

The answer, you see, is shared memory, but let's back up a bit and see
the process.

1) I need to select a record from the TicketsOpen table and I happen
to know it's ID. This record has an ID column with a guaranteed unique
ID, and a tracking number (TN) with an approximate unique ID.

(The TN is short and is used to give out to customers. It cannot be
guessed easily, so customers cannot fib. We only use the TN ID when we
want to zoom in on the record approximately. We have to ask further
questions to ensure we are working with the right ticket. The
algorithm for TN is based on second, month, day, and a small random
number.)

2) Okay, now I need to void this record, so I need to move this to the
TicketsVoided table.

(Why not just have a Void bit on the record in the TicketsOpen table?
Well, there's two reasons: (a) table size is an issue, and (b) we want
faster SELECTS. Even though I can restrict a SELECT to ignore a Void
bit, it still is faster if there are no voided records in the opened
tickets table. You see?)

I now do a SELECT MAX(ID) FROM TicketsVoided. And, of course, I want
to increment that var with ++. But that won't work, will it? I mean,
another user could be on this same web page, doing the same thing with
another record, and we could end up clobbering each other's records.

The solution is to use shared memory in PHP. I create a shared memory
variable that stores my username if I'm currently doing the write
process to the target table. Then, when I'm finished, I clear the
variable to nothing. So, before any user can do SELECT MAX(ID)...,
they must wait in a small loop until the shared memory var is free.
Then, they can stick their username in there. While waiting, I use
usleep(500) to sleep a series of half seconds, up to 4 seconds max,
until this var is free. If that 4 seconds is up, then I assume
something is wrong and I just go ahead and let the user use the shared
memory var and start doing SELECT MAX(ID).

3) Okay, now that I have guaranteed without a doubt 100% that I now
have the latest ID for a record to insert into TicketsVoided, I do an
INSERT to write it there.

4) I now delete the record from the source table (TicketsOpen).

5) Now the record is moved, so I clear the shared memory variable,
freeing up any other process that's waiting to have the permission to
do a move to this table.

In a sense, shared memory is my workaround for transactions.

And so why didn't I do this with MySQL's transactions, or another
database? Because I'm trying to stay ANSI-92 here, and I want to
produce something that could use almost any database backend,
including PostgreSQL, MS SQL, Oracle, etc.
Jul 17 '05 #3

On 10-Nov-2003, go********@hotpop.com (Google Mike) wrote:
Thanks to the programmers of PHP, they made the SharedMemory API. That
solved the problem. You see, it appears that the API in PHP that
writes to MySQL works somewhat asynchronously sometimes, especially
when forced by someone hitting pages very rapidly. If one wants to do
an insert, followed by an update, they may end up with two different
records in some cases where they don't use the unique ID column, but
some other column or set of columns. The resolution I used was to make
one single insert with everything, including the unique ID, but the
trick question I had for awhile was, "How do I ensure that I'm going
to be the one holding the record with the unique ID, and not some
other process take that ID?"

The answer, you see, is shared memory, but let's back up a bit and see
the process.

1) I need to select a record from the TicketsOpen table and I happen
to know it's ID. This record has an ID column with a guaranteed unique
ID, and a tracking number (TN) with an approximate unique ID.

(The TN is short and is used to give out to customers. It cannot be
guessed easily, so customers cannot fib. We only use the TN ID when we
want to zoom in on the record approximately. We have to ask further
questions to ensure we are working with the right ticket. The
algorithm for TN is based on second, month, day, and a small random
number.)

2) Okay, now I need to void this record, so I need to move this to the
TicketsVoided table.

(Why not just have a Void bit on the record in the TicketsOpen table?
Well, there's two reasons: (a) table size is an issue, and (b) we want
faster SELECTS. Even though I can restrict a SELECT to ignore a Void
bit, it still is faster if there are no voided records in the opened
tickets table. You see?)

I now do a SELECT MAX(ID) FROM TicketsVoided. And, of course, I want
to increment that var with ++. But that won't work, will it? I mean,
another user could be on this same web page, doing the same thing with
another record, and we could end up clobbering each other's records.

The solution is to use shared memory in PHP. I create a shared memory
variable that stores my username if I'm currently doing the write
process to the target table. Then, when I'm finished, I clear the
variable to nothing. So, before any user can do SELECT MAX(ID)...,
they must wait in a small loop until the shared memory var is free.
Then, they can stick their username in there. While waiting, I use
usleep(500) to sleep a series of half seconds, up to 4 seconds max,
until this var is free. If that 4 seconds is up, then I assume
something is wrong and I just go ahead and let the user use the shared
memory var and start doing SELECT MAX(ID).

3) Okay, now that I have guaranteed without a doubt 100% that I now
have the latest ID for a record to insert into TicketsVoided, I do an
INSERT to write it there.

4) I now delete the record from the source table (TicketsOpen).

5) Now the record is moved, so I clear the shared memory variable,
freeing up any other process that's waiting to have the permission to
do a move to this table.

In a sense, shared memory is my workaround for transactions.

And so why didn't I do this with MySQL's transactions, or another
database? Because I'm trying to stay ANSI-92 here, and I want to
produce something that could use almost any database backend,
including PostgreSQL, MS SQL, Oracle, etc.


An alternative to the shared memory would be a table to store the used ids,
just add a row and retrieve the id it generated. If someone else is doing
the same they'll get a different row/id.

--
Tom Thackrey
www.creative-light.com
tom (at) creative (dash) light (dot) com
do NOT send email to ja*********@willglen.net (it's reserved for spammers)
Jul 17 '05 #4
"Tom Thackrey" <us***********@nospam.com> wrote
An alternative to the shared memory would be a table to store the used ids,
just add a row and retrieve the id it generated. If someone else is doing
the same they'll get a different row/id.


Yeah. No duh. Man, I must have been stupid. I forgot that I want this
app to be web-farmable, and have taken every effort to make all
session state be in encrypted browser cookies. Well, when I have to
use shared memory to manage transaction queues of users trying to move
records between the various tables, I completely forgot that this only
works in a one-server environment. So, it looks I'm going to have to
use the idea you suggest. Of course, if I didn't have this MySQL
problem where the INSERTS and UPDATES didn't seem to run
asynchronously in a web page if the page is hammered, I wouldn't need
this, but I guess I'm going to need this.
Jul 17 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Bruce A. Julseth | last post by:
My MySQL.user table (user, host, password) looks like the following: +---------+-----------+---------------------+ | user | host | password |...
14
by: smilesinblues | last post by:
Hi all, I have a table with a lot of songs. All songs have a field called hits. Everytime a song page is seen hits becomes hit++, everything smooth going. I also have a page where I run the...
0
by: Neculai Macarie | last post by:
Hi! Using Union and Order By gives strange behaviour in the following test-case: drop table if exists gallery; drop table if exists gallery_categ; # create test tables create table gallery...
0
by: Nedelcho Stanev | last post by:
Hello All, I have strange problem with libodbc++ ( 0.2.3 or 0.2.2 ). i'm using mysql-4.0.14 , MyODBC-3.51.06 and unixODBC-2.2.6 configured with following options 1.MySQL ../configure...
0
by: Steve | last post by:
Hi, Yesterday I signed up for another dedicated linux server with the hosting company I use and I am having problems getting MySQL 4.1.3 working on it. I've tried just about everything I can...
3
by: Sven Reifegerste | last post by:
Hi, i have a table with INT columns id,key,b1,b2,c1,c2, having 1.500.000 rows. 'key' and 'id' are indexed (Kardinality 385381) and id (Kardinality 1541525). Performing a SELECT * FROM...
4
by: 21novembre | last post by:
Hi all, I got a quite strange problem when I tried to setup a database backup shell. I put it this way: "bin/mysqldump --opt --user=xxx --password=xxx DB > DB.bak" However, error 1045 came to...
11
by: DJJ | last post by:
I am using the MySQL ODBC 3.51 driver to link three relatively small MySQL tables to a Microsoft Access 2003 database. I am finding that the data from the MySQL tables takes a hell of a long time...
9
by: Dave | last post by:
Hi guys, I have just set up a duplicate server running: apache 2.54, mysql 5.04 and php 5.04 This is the same setup as as the server we are using now, apart from the hardware inside. I have...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.