InnoDB: Why does transaction wait for IX lock when it already has a X lock? - innodb

I'm getting a deadlock in my code. Txn 1 is waiting for a lock to be granted which is held currently by Txn 2. Txn 2 already has a X lock but still is requesting for a IX lock.
Both the transactions run an Insert query using ActiveRecord import.
The deadlock section in SHOW ENGINE INNODB STATUS gives me:
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147861 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
My question is why does Txn 2 need an IX lock if it already holds an X lock?
Update 1:
Here is the complete LATEST DETECTED DEADLOCK section:
------------------------
LATEST DETECTED DEADLOCK
------------------------
2022-09-02 16:11:22 0x149b2c2ef700
*** (1) TRANSACTION:
TRANSACTION 51241147861, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 8 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 8423321, OS thread handle 22689954051840, query id 19045173171 172.31.15.180 mitdb4dm1n update
INSERT INTO `user_metrics` (`id`,`current`,`total`,`my_type`,`owner_id`,`owner_type`,`created_at`,`updated_at`) VALUES (NULL,175,175,0,108840,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,100,151,0,108841,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,169,169,0,112780,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,202,217,0,112781,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,26,62,0,112782,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,169,169,0,112794,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,177,217,0,112795,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,28,62,0,112796,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,140,140,0,114162,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,64,64,0,114163,'OwnerName','2022-09-0
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147861 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) TRANSACTION:
TRANSACTION 51241147863, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
7 lock struct(s), heap size 1136, 5 row lock(s), undo log entries 1
MySQL thread id 8424694, OS thread handle 22656693761792, query id 19045173179 172.31.4.27 mitdb4dm1n update
INSERT INTO `user_metrics` (`id`,`current`,`total`,`my_type`,`owner_id`,`owner_type`,`created_at`,`updated_at`) VALUES (NULL,1,89,0,137623,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,0,3,0,137624,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,178,182,0,137635,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,77,129,0,137645,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,5,14,0,137646,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,0,87,0,137656,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,0,11,0,137657,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,71,71,0,146601,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,71,71,0,146616,'OwnerName','2022-09-02 16:11:22','2022-09-02 16:11:22'),(NULL,39,64,0,146631,'OwnerName','2022-09-02 16:11:22','2022-0
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7431 page no 178 n bits 80 index PRIMARY of table `company_ebdb`.`user_metrics` trx id 51241147863 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
*** WE ROLL BACK TRANSACTION (2)
Update 2:
Create table output for the table involved:
CREATE TABLE `user_metrics` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`current` decimal(10,0) DEFAULT NULL,
`total` decimal(10,0) DEFAULT NULL,
`my_type` int(11) DEFAULT NULL,
`owner_id` int(11) DEFAULT NULL,
`owner_type` varchar(255) DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_user_metrics_on_owner_type_and_owner_id_and_my_type` (`owner_type`,`owner_id`,`my_type`)
) ENGINE=InnoDB AUTO_INCREMENT=1167 DEFAULT CHARSET=utf8mb4

Plan A:
Live with it. But do catch the error and replay the INSERT.
Plan B:
Try this. (I have no confidence that it will help):
You have two Unique keys (the PK is one). Let's switch them around, to the following. (It assumes you can change all three columns to NOT NULL.)
PRIMARY KEY(`owner_type`,`owner_id`,`my_type`),
INDEX(id)
Rationale:
Two Unique keys leads to two things being locked, and more than twice the likelihood of a conflict.
Having the data clustered in the order that is beneficial to the query will speed up the query, hence making it more likely to finish before conflicting with another connection.
I doubt if either of these will be sufficient to prevent deadlocks. But they may decrease the frequency of deadlocks. Hence, plan doing Plan A, too.

Related

Select record conditionally by comparing date and time

Using SQLite, how do I retrieve the first record where current time is between record start and stop times, OR ELSE, if current time is outside start/stop times of all existing records, instead retrieve the record that comes next in time after current local time?
Here's my table:
TABLE sessions (
session_id INTEGER PRIMARY KEY AUTOINCREMENT,
session_start REAL,
session_stop REAL,
group_id INTEGER REFERENCES [groups] (group_id) ON DELETE CASCADE
);
Example data:
session_id session_start session_stop group_id
1 2459599.755438762 2459599.963772095 1
2 2459600.0471054283 2459600.1721054283 2
3 2459600.755438762 2459600.963772095 1
The session table has a foreign key, group_id, but for sake of the example maybe we can leave it aside.
EDIT:
Tried mankowitz suggestion within a small test application:
SQL.Text:='SELECT *, julianday(''Now'',''localtime'') AS julian_now FROM sessions '
+'WHERE session_start > julian_now '
+'ORDER BY session_stop < julian_now LIMIT 1';
Current time at runtime was 2022-01-17 05:40:45 and the record with session_id 16 should have been selected since it is the closest to in time. How to modify the code for this?
For the condition:
current time is between record start and stop times
you need this boolean expression in the ORDER BY clause:
julianday('now', 'localtime') BETWEEN session_start AND session_stop
and for the condition:
OR ELSE, if current time is outside start/stop times of all existing
records, instead retrieve the record that comes next in time after
current local time
you need this:
julianday('now', 'localtime') > session_stop
Write your query like this:
SELECT *
FROM sessions
ORDER BY julianday('now', 'localtime') BETWEEN session_start AND session_stop DESC,
julianday('now', 'localtime') > session_stop DESC,
session_start
LIMIT 1;
select * from sessions where sesson_start>{currenttime}
order by session_end<{currenttime}
limit 1

ORA-01465: invalid hex number, when inserting data to oracle table

I'm working under Oracle, and when want to insert a simple insert query I got the below error
ORA-01465: invalid hex number
Knowing that I don't have any specific column in my table.
I have tried lot of solutions on the web but without any success.
INSERT INTO LIMITE_AGENC (OBJECTID_1, OBJECTID, NOM_AGENCE, SURFACE, BASSIN,
NOM, SHAPE_LENG, ID_REG, ORDRE_ABH)
VALUES
(30, 25, 'Agence', 13591, 'Hydraulique',
'Loukkomoti', 8.12883522, 12, 3);
and here is my table script, I have updated the script with more details if you need to check the index part...
DROP TABLE LIMITE_AGENC CASCADE CONSTRAINTS;
CREATE TABLE LIMITE_AGENC
(
OBJECTID_1 INTEGER,
OBJECTID INTEGER,
NOM_AGENCE NVARCHAR2(80),
SURFACE INTEGER,
BASSIN NVARCHAR2(100),
NOM NVARCHAR2(50),
SHAPE_LENG NUMBER(38,8),
SHAPE SDE.ST_GEOMETRY,
ID_REG INTEGER,
ORDRE_ABH INTEGER
)
LOB ("SHAPE"."POINTS") STORE AS BASICFILE (
TABLESPACE USERS
ENABLE STORAGE IN ROW
CHUNK 8192
PCTVERSION 10
NOCACHE
LOGGING
STORAGE (
INITIAL 64K
NEXT 1M
MINEXTENTS 1
MAXEXTENTS UNLIMITED
PCTINCREASE 0
BUFFER_POOL DEFAULT
))
TABLESPACE USERS
PCTUSED 0
PCTFREE 10
INITRANS 1
MAXTRANS 255
STORAGE (
INITIAL 64K
NEXT 1M
MAXSIZE UNLIMITED
MINEXTENTS 1
MAXEXTENTS UNLIMITED
PCTINCREASE 0
BUFFER_POOL DEFAULT
)
LOGGING
NOCOMPRESS
NOCACHE
MONITORING;
CREATE UNIQUE INDEX UNIQ_CONST_AGENCE2 ON LIMITE_AGENC
(OBJECTID)
LOGGING
TABLESPACE USERS
PCTFREE 10
INITRANS 2
MAXTRANS 255
STORAGE (
INITIAL 64K
NEXT 1M
MAXSIZE UNLIMITED
MINEXTENTS 1
MAXEXTENTS UNLIMITED
PCTINCREASE 0
BUFFER_POOL DEFAULT
);
ALTER TABLE LIMITE_AGENC ADD (
CONSTRAINT UNIQ_CONST_AGENCE2
UNIQUE (OBJECTID)
USING INDEX UNIQ_CONST_AGENCE2
ENABLE VALIDATE);
GRANT SELECT ON LIMITE_AGENC TO SDE;
Hi I have found the issue, so the problem is that I have to add the geometry column Shape in my request with an empty value as below
Insert into LIMITE_AGENC (OBJECTID_1, OBJECTID, NOM_AGENCE, SURFACE, BASSIN,
NOM, SHAPE_LENG,SHAPE, ID_REG, ORDRE_ABH)
Values
(30, 25, 'Agence', 13591, 'Hydraulique',
'Loukkomoti', 8.12883522, '', 12, 3);
I hope that would help someone in the future

oracle 11g application express interface virtual column that calculates total amount of sales

Im currently working on my interface project by editing a certain page item of a form in oracle 11g application express. Im in confusion that one of the column is derived from another column in the table, does not work properly when i tried to register a new data through the interface. the column cannot calculate the derived data just like how it works in the oracle sql developer. i set the column as follows:
Im clueless, that should i display the column as hidden, or it has something to do with the source settings that needs PL/SQL expression to calculate the values of quantity, and cost_perunit column for total_amount values automatically. I have searched the web for the solution, but cant find a solution and a related issue to this.
this command was uploaded in a text .txt format into the application express
CREATE TABLE PAYMENT(
PAY_ID NUMBER(25)NOT NULL,
PAY_DATE DATE,
PAY_METHOD VARCHAR2(50 char),
SPARE_TYPE VARCHAR2(50 char),
QUANTITY NUMBER(12),
COST_PERUNIT NUMBER(6,2),
TOTAL_AMOUNT as (quantity*cost_perunit),
CONSTRAINT PAY_PK PRIMARY KEY(PAY_ID)
);
CREATE SEQUENCE pay_id_seq START WITH 400;
Im referring to the TOTAL_AMOUNT as (quantity*cost_perunit) column that returns error when i try to create a new data through the form. what should i change in the page edit settings, so it will work as it supposed to be.
You have two choices create a table without virtual column calculate total amount at run time
e.g select a.*,(quantity*cost_perunit) total_amount from payment a;
or create table with virtual column with keyword virtual
CREATE TABLE PAYMENT(
PAY_ID NUMBER(25)NOT NULL,
PAY_DATE DATE,
PAY_METHOD VARCHAR2(50 char),
SPARE_TYPE VARCHAR2(50 char),
QUANTITY NUMBER(12),
COST_PERUNIT NUMBER(6,2),
TOTAL_AMOUNT as (quantity*cost_perunit) VIRTUAL,
CONSTRAINT PAY_PK PRIMARY KEY(PAY_ID));
#ddl payment
DBMS_METADATA.GET_DDL(OBJECT_TYPE,OBJECT_NAME,OWNER)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE TABLE "SCOTT"."PAYMENT"
( "PAY_ID" NUMBER(25,0) NOT NULL ENABLE,
"PAY_DATE" DATE,
"PAY_METHOD" VARCHAR2(50 CHAR),
"SPARE_TYPE" VARCHAR2(50 CHAR),
"QUANTITY" NUMBER(12,0),
"COST_PERUNIT" NUMBER(6,2),
"TOTAL_AMOUNT" NUMBER GENERATED ALWAYS AS ("QUANTITY"*"COST_PERUNIT") VIRTUAL ,
CONSTRAINT "PAY_PK" PRIMARY KEY ("PAY_ID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
TABLESPACE "EXAMPLE" ENABLE
) SEGMENT CREATION DEFERRED
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
TABLESPACE "EXAMPLE" ;
SQL> insert into payment (pay_id,pay_date,pay_method,spare_type,quantity,cost_perunit)
2 values
3 (&pid,&pdt,&pmethod,&spare,&qty,&cpu);
Enter value for pid: 101
Enter value for pdt: sysdate
Enter value for pmethod: 'CASH'
Enter value for spare: 'CREDIT CARD'
Enter value for qty: 12
Enter value for cpu: 1.9
1 row created.
Elapsed: 00:00:00.10
SQL> /
Enter value for pid: 102
Enter value for pdt: sysdate-1
Enter value for pmethod: 'DEBIT CARD'
Enter value for spare: 'CREDIT CARD'
Enter value for qty: 21
Enter value for cpu: 2.99
1 row created.
Elapsed: 00:00:00.00
SQL> commit;
Commit complete.
SQL> select * from payment;
PAY_ID PAY_DATE PAY_METHOD SPARE_TYPE QUANTITY COST_PERUNIT TOTAL_AMOUNT
---------- ------------------- -------------------------------------------------- -------------------------------------------------- ---------- ------------ ------------
101 2020-06-25 14:06:46 CASH CREDIT CARD 12 1.9 22.8
102 2020-06-24 14:07:29 DEBIT CARD CREDIT CARD 21 2.99 62.79
SQL>
Edit:I have no idea about apex but if you decide to create the table without virtual column you can add label box or non editable text box item and use formula to display calculation for apex forms run time

Why is SQLite insertion cost linear?

I had assumed that the time cost of insertion is log to the number of records. But my test in SQLite 3.22 seems showing it is linear. Note both X/Y are in log scale.
The size(k) column is the number of rows I inserted at each test. Its unit is K. I did 3 tests. Journal and synchronous are off. Locking_mode is exclusive. All operations are included in one transaction.
time1
create table t1 (id primary key, name text);
create index nameIdx on t1(name)
// for i = [1:<size>]
// insert into t1 values(i, "foo"i)
create table t2 (id primary key, value int);
// for i = [1:<size>]
// insert into t2 values(i, i)
time2
create table t1 (id primary key, name text);
// for i = [1:<size>]
// insert into t1 values(i, "foo"i)
create index nameIdx on t1(name)
create table t2 (id primary key, value int);
// for i = [1:<size>]
// insert into t2 values(i, i)
time3
create table t1 (id primary key, name text, value int);
// for i = [1:<size>]
// insert into t1 values(i, "foo"i, 0)
create index nameIdx on t1(name)
// for i = [1:<size>]
// update t1 set value=0 where id=<i>
All the 3 test cases have similar costs. They seem linear.
Also I had thought case 3 can be faster because update does not need rebalance tree or add new records. But case3 is a little bit slower...
Are the results expected? Maybe my input data are too small to see the log complexity?
SQLite optimizes inserts at the end of the table. sqlite3BtreeInsert() in btree.c has:
/* If the cursor is currently on the last row and we are appending a
** new row onto the end, set the "loc" to avoid an unnecessary
** btreeMoveto() call */
To get worse run time, try inserting the rows in random order, or at least inserting the a very large value first.
Anyway, the run time is dominated by disk I/O, and the non-leaf pages are most likely to be cached. Use more transactions (so that all pages need to be flushed to disk), or use an in-memory database.

SQLite ignoring calculation used in with a WHERE statement

I'm trying to use an SQLite query to calculate when an alarm should become active. I store the start time and the detent (the timeout or delay before the alarm becomes active) in the same table as seconds. Here is the schema:
CREATE TABLE alarm_log (
scope integer NOT NULL, --REFERENCES scopes_inst(inst)
start integer NOT NULL, -- seconds
end integer,
severity text NOT NULL,
value text NOT NULL,
details text,
raised_by integer, -- the scope id that raised the alarm
detent integer, -- detent in seconds
PRIMARY KEY (scope, start)
);
And here is the query that I am trying to use to pull the active alarms using some simple math:
SELECT *,
(start + detent) AS activated, -- the time that the alarm becomes active
STRFTIME("%s","now") AS now -- the current time of the query
FROM alarm_log
WHERE end IS NULL
AND now > activated; -- ensure that the alarm is activated
I have created an alarm with a detent of 30 seconds. The start time is 1378870712 so it should become active at 1378870742 (see the activated column).
This is an example of the above query returning rows 4 seconds before the activated time...
scope start end severity value details raised_by detent activated now
---------- ---------- ---------- ---------- --------------------- ---------------- ---------- ---------- ---------- ----------
4 1378870712 warning out-of-range too-high min 500 max 1500 0 30 1378870742 1378870738
Now the same query returns that row 2 seconds after the alarm was activated.
scope start end severity value details raised_by detent activated now
---------- ---------- ---------- ---------- --------------------- ---------------- ---------- ---------- ---------- ----------
4 1378870712 warning out-of-range too-high min 500 max 1500 0 30 1378870742 1378870744
I would expect reversing the > on the last line to a < it should show alarms that are not active... but this returns nothing, before or after a detent time passes...
Is this normal behaviour? The query looks fine to me. Thanks for reading :)
EDIT:
I should have mentioned that I also tried it with the calculations straight in the WHERE clause, but it does the same thing :(
SELECT *,
(start + detent) AS activated, -- the time that the alarm becomes active
STRFTIME("%s","now") AS now -- the current time of the query
FROM alarm_log
WHERE end IS NULL
AND STRFTIME("%s","now") > (start + detent);
EDIT2:
Here's some test data:
SQLite styles:
scope|start|end|severity|value|details|raised_by|detent
4|1378935271|1378935842|warning|out-of-range too-high|min 500 max 1500|0|600
4|1378935854|1378935876|warning|out-of-range too-high|min 500 max 1500|0|600
4|1378935884||warning|out-of-range too-high|min 500 max 1500|0|600
CSV styles
scope,start,end,severity,value,details,raised_by,detent
4,1378935271,1378935842,warning,out-of-range too-high,min 500 max 1500,0,600
4,1378935854,1378935876,warning,out-of-range too-high,min 500 max 1500,0,600
4,1378935884,,warning,out-of-range too-high,min 500 max 1500,0,600
Try this:
select * from (
select *, (start + detent) as activated,
strftime("%s","now") as now
from alarm_log where end is null
) where cast(now as int) > activated;

Resources