Сериализация двух последовательных транзакций в хранимой процедуре MySQL - PullRequest
0 голосов
/ 16 января 2020

TL; версия DR:

У меня есть две транзакции в хранимой процедуре MySQL. Второй зависит от данных, вставленных первым. Я использую SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; и COMMIT AND CHAIN в конце первой транзакции, но вторая транзакция, кажется, не видит данные первой.

Если после этого я выполню тот же запрос, что и во второй транзакции, он выполнит то, что ожидал. Поэтому мне нужно, чтобы вторая транзакция действительно ожидала завершения первой.

Подробности:

У меня есть хранимая процедура в MySQL 5.6 по существу, создается запись в таблице, в которой перечислены некоторые элементы, а затем создается копия данных, относящихся к родительскому элементу. Затем эти данные объединяются в другую таблицу, которая не изменяется.

В этом примере:

r_subject -1: N-> put_test_join -1: 1-> put_test_data

put_test_bundles также имеет отношение к put_test_join, по существу, чтобы идентифицировать имя тестового пакета для заданных данных. Меня не особенно беспокоит структура базы данных, а скорее проблема, возникающая у меня с процедурой:

create procedure create_branch(IN _branch_name varchar(255), IN _project varchar(255),
                                                       IN _subver varchar(255), IN _revision int(10),
                                                       IN _parent varchar(255), IN _owner varchar(255))
BEGIN
  DECLARE done INT DEFAULT FALSE;
  DECLARE parent_id INT UNSIGNED;
  DECLARE new_tbid INT UNSIGNED;
  DECLARE _rid INT UNSIGNED;
  DECLARE _pgid INT UNSIGNED;
  DECLARE _pdid INT UNSIGNED;
  DECLARE _dupe_id INT UNSIGNED;
  DECLARE _rfu_id INT UNSIGNED;
  DECLARE ptj_cursor CURSOR FOR SELECT rid,pgid,pdid,dupe_id,rfu_id FROM put_test_join WHERE tbid = parent_id;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

  DECLARE exit handler for SQLEXCEPTION
  BEGIN
    GET DIAGNOSTICS CONDITION 1 @sqlstate = RETURNED_SQLSTATE,
      @errno = MYSQL_ERRNO, @text = MESSAGE_TEXT;
    SET @full_error = CONCAT('ERROR ', @errno, ' (', @sqlstate, '): ', @text);
    SELECT @full_error;
  END;

  IF (SELECT COALESCE(id) FROM put_test_bundles WHERE put_test_bundles.name = _branch_name AND project = _project AND subver = _subver LIMIT 1) IS NOT NULL THEN
    SIGNAL SQLSTATE '45000'
      SET MESSAGE_TEXT = 'This branch already exists';
  END IF; # Check for dupes

  SELECT id INTO parent_id FROM put_test_bundles WHERE name = _parent AND project = _project AND subver = _subver;

  IF (parent_id IS NULL) THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = 'Parent not found';
  END IF;

  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

  OPEN ptj_cursor;
  START TRANSACTION;
    INSERT INTO put_test_bundles (name, project, subver, revision, parent, owner, istest, locked) VALUES (_branch_name, _project, _subver, _revision, parent_id, _owner, 0, 0);
    SET new_tbid = LAST_INSERT_ID();
    ptd_loop: LOOP
      IF done THEN
        LEAVE ptd_loop;
      END IF;
      FETCH NEXT FROM ptj_cursor INTO _rid, _pgid, _pdid, _dupe_id, _rfu_id;
      INSERT INTO put_test_data (location_override, bit_override, test_name, pe_description, trimtest, tr_skip, td_skip, tbd, test_in, test_out, test_out_p2, test_out_p3, test_out_p4, mask_blow, high, low, step, default_setting, operating_value, mask_address, mask_row, iteration, username, updatedBy) SELECT location_override, bit_override, test_name, pe_description, trimtest, tr_skip, td_skip, tbd, test_in, test_out, test_out_p2, test_out_p3, test_out_p4, mask_blow, high, low, step, default_setting, operating_value, mask_address, mask_row, iteration, username, updatedBy FROM put_test_data WHERE id = _pdid;
      INSERT INTO put_test_join (rid, tbid, pgid, pdid, dupe_id, rfu_id) VALUES (_rid, new_tbid, _pgid, LAST_INSERT_ID(), _dupe_id, _rfu_id);
    END LOOP;
  COMMIT AND CHAIN;
  CLOSE ptj_cursor;
    UPDATE put_test_data JOIN
    (SELECT parent.id as parent_id,dupe.id as dupe_id FROM
    (SELECT t.name,r.project,r.location,r.location_bit,d.id
        FROM put_test_data d
            JOIN put_test_join ptj on d.id = ptj.pdid
            JOIN r_subject r ON ptj.rid = r.rid
            JOIN put_test_bundles t ON ptj.tbid = t.id
        WHERE t.name = _branch_name and r.project = _project FOR UPDATE) parent
    JOIN
    (SELECT t.name,p.project,p.location,p.location_bit,p.functionality,d.id,d.dupe_parent,d.dupe_diverged
        FROM put_test_data d
            JOIN put_test_join ptj on d.id = ptj.pdid
            JOIN duplicates p ON ptj.dupe_id = p.rid
            JOIN put_test_bundles t ON ptj.tbid = t.id
        WHERE t.name = _branch_name and p.project = _project) dupe
                                                    ON parent.name                  = dupe.name
                                                    AND parent.project              = dupe.project
                                                    AND parent.location        = dupe.location
                                                    AND parent.location_bit    = dupe.location_bit
    WHERE parent.project = _project
      AND parent.name = _branch_name) s ON put_test_data.id = s.dupe_id
    SET put_test_data.dupe_parent = s.parent_id WHERE put_test_data.dupe_parent IS NULL AND s.parent_id IS NOT NULL;
  COMMIT;
END;

По сути, это создает запись в put_test_bundles, а затем копирует все связанные родительские данные. и связывает их с новым тестовым пакетом и существующим r_subject.

Эта транзакция работает просто отлично, но после того, как она завершена, мне нужно просмотреть данные для идентификаторов данных, которые имеют дублирующуюся функциональность и "ссылку "их вместе. Я думаю, что это не удается, потому что вторая транзакция не может видеть данные первой транзакции. Я использую SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; и COMMIT AND CHAIN в конце первой транзакции, но вторая транзакция не работает. Если я выполняю точно такой же запрос, как во второй транзакции, он выполняет то, что я ожидаю. Поэтому мне нужно, чтобы вторая транзакция действительно ожидала завершения первой.

Обновление: Я вызываю эту хранимую процедуру из парусов. js application. Даже когда я инкапсулировал второй запрос в другую хранимую процедуру и назвал их так:

let result = await sails.sendNativeQuery('CALL create_branch($1, $2, $3, $4, $5, $6);', [branch, project, subver, 0, parent, user]);
let linkCount = await sails.sendNativeQuery('CALL link_dupes($1, $2);', [project, branch]);

Вторая процедура все еще не видит данные из первой. Как я могу гарантировать, что первая процедура полностью зафиксирована и данные доступны для чтения перед выполнением второго запроса?

...