[MCOL-1662] WriteEngine bulk methods do not version dictionaries correctly Created: 2018-08-20  Updated: 2023-10-26  Resolved: 2019-01-04

Status: Closed
Project: MariaDB ColumnStore
Component/s: None
Affects Version/s: 1.1.5, 1.2.1
Fix Version/s: 1.1.7, 1.2.3

Type: Bug Priority: Critical
Reporter: markus makela Assignee: Patrick LeBlanc (Inactive)
Resolution: Fixed Votes: 1
Labels: None
Environment:

single node docker image


Issue Links:
Duplicate
is duplicated by MCOL-930 Cannot execute queries Closed
is duplicated by MCOL-1278 occasionally IDB-2031: Blocks are mis... Closed
Sprint: 2018-16, 2018-17, 2018-18, 2018-19, 2018-20, 2018-21

 Description   

The following test case creates a table, inserts a row into it via mcsapi and updates it after the bulk load has committed.

#include <iostream>
#include <string>
#include <sstream>
 
#include <mysql.h>
#include <libmcsapi/mcsapi.h>
 
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
namespace pt = boost::property_tree;
 
int main(int argc, char** argv)
{
    if (argc != 3)
    {
        std::cout << "Usage: DATABASE TABLE" << std::endl
                  << "Columnstore.xml must be located in /usr/local/mariadb/columnstore/etc/Columnstore.xml" << std::endl;
        return 1;
    }
 
    // Read the credentials from the Columnstore.xml
    pt::ptree tree;
    pt::read_xml("/usr/local/mariadb/columnstore/etc/Columnstore.xml", tree);
    std::string host = tree.get<std::string>("Columnstore.CrossEngineSupport.Host");
    int port = tree.get<int>("Columnstore.CrossEngineSupport.Port");
    std::string user = tree.get<std::string>("Columnstore.CrossEngineSupport.User");
    std::string password = tree.get<std::string>("Columnstore.CrossEngineSupport.Password");
    std::string database = argv[1];
    std::string table = argv[2];
 
    // Connect to CS and create the table
    auto mysql =  mysql_init(nullptr);
    mysql_real_connect(mysql, host.c_str(), user.c_str(),
                       password.empty() ? NULL : password.c_str(),
                       NULL, port, NULL, 0);
 
    std::stringstream query;
    query << "CREATE TABLE " << database << "." << table << "(id int, data varchar(200)) ENGINE=Columnstore";
    mysql_query(mysql, query.str().c_str());
 
    // Connect with mcsapi and insert one row with a bulk insert
    auto driver = new mcsapi::ColumnStoreDriver(cnf);
    auto bulk = driver->createBulkInsert(database, table, 0, 0);
    auto info = driver->getSystemCatalog().getTable(database, table);
    uint32_t pos1 = info.getColumn("id").getPosition();
    uint32_t pos2 = info.getColumn("data").getPosition();
    bulk->setColumn(pos1, 1);
    bulk->setColumn(pos2, "hello");
    bulk->writeRow();
    bulk->commit();
 
    // Update the row we just inserted
    std::stringstream query;
    query << "UPDATE `" << database << "`.`" << table << "` SET `id` = 1, `data` = 'Hello' WHERE `id` = 1 AND `data` = 'hello'";
    mysql_query(mysql, query.str().c_str());
 
    return 0;
}

Once the update is done, the data in the table is corrupted:

MariaDB [test]> select * from test.t1;
+------+----------+
| id   | data     |
+------+----------+
|    1 | _CpNoTf_ |
+------+----------+
1 row in set (0.05 sec)

Manually updating it afterwards doesn't fix it, it still remains invalid:

MariaDB [test]> update test.t1 set data = "Hello" where id = 1;
Query OK, 1 row affected (0.15 sec)
Rows matched: 0  Changed: 0  Warnings: 0
 
MariaDB [test]> select * from test.t1;
+------+----------+
| id   | data     |
+------+----------+
|    1 | _CpNoTf_ |
+------+----------+
1 row in set (0.02 sec)

Additional note from Andrew: This also happens when using INSERT...SELECT and LDI when cpimport mode is not used.



 Comments   
Comment by markus makela [ 2018-08-20 ]

Deleting bulk and driver has no effect and neither does reconnecting before updating.

Comment by Andrew Hutchings (Inactive) [ 2018-08-28 ]

workaround, execute this at the end:

select calflushcache();

Comment by Andrew Hutchings (Inactive) [ 2018-08-28 ]

My notes:
I think bulkSetHWMandCP second half the of the packet is incorrect which sets the incorrect state after the bulk import. This could cause issues for the system catalog during UPDATE and therefore PrimProc.

Comment by Dipti Joshi (Inactive) [ 2018-11-21 ]

markus makela Have you tried the work around that Andrew proposed on August 28th

Comment by Andrew Hutchings (Inactive) [ 2018-11-26 ]

This is a much wider problem than I first anticipated:

MariaDB [test]> CREATE TABLE IF NOT EXISTS mcol1662 (a int, b varchar(200)) engine=columnstore;
Query OK, 0 rows affected (2.44 sec)
 
MariaDB [test]> create table source (a int, b varchar(200));
Query OK, 0 rows affected (0.17 sec)
 
MariaDB [test]> insert into source values (1, 'hello');
Query OK, 1 row affected (0.04 sec)
 
MariaDB [test]> set infinidb_use_import_for_batchinsert=0;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [test]> insert into mcol1662 select * from source;
Query OK, 1 row affected (0.67 sec)
Records: 1  Duplicates: 0  Warnings: 0
 
MariaDB [test]> UPDATE mcol1662 SET a = 1, b = 'Hello' WHERE a = 1 and b = 'hello';
Query OK, 1 row affected (0.52 sec)
Rows matched: 0  Changed: 0  Warnings: 0
 
MariaDB [test]> SELECT * FROM mcol1662;
+------+----------+
| a    | b        |
+------+----------+
|    1 | _CpNoTf_ |
+------+----------+
1 row in set (0.04 sec)

Comment by Andrew Hutchings (Inactive) [ 2018-11-26 ]

This is a regression from 1.0 -> 1.1

Comment by Andrew Hutchings (Inactive) [ 2018-11-26 ]

The regression in INSERT...SELECT is due to switching away from using versioning for bulk inserts in write engine. I highly suspect we need to add extra checkpointing information in the BRM to fix this properly.

Comment by Andrew Hutchings (Inactive) [ 2018-12-14 ]

Looks like dictionaries don't get versioned correctly. writeVBEntry() isn't called on them so they get stuck on version 0.

Comment by Andrew Hutchings (Inactive) [ 2018-12-17 ]

Three branches for this one:

  • API: test case
  • regression suite: test case
  • engine: fix

UPDATE queries were not updating the version number on dictionary blocks which was causing PrimProc cache to think they hadn't been touched, hence the bad cache issue. UPDATE is the perfect candidate for this because it does a PrimProc read/cache before updating the entry.

The fix makes sure that version buffer is turned on for update queries, therefore allowing the block version to be bumped on commit.

For QA: there is a test in the comments which has been converted into a regression test. There is also a test in the API's test suite.

Generated at Thu Feb 08 02:30:25 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.