2016-07-25

Staсkedit hangs on starting

В нижеуказанном Stackedit случилась небольшая неприятность.
После попытки импортировать страницу (Import URL) он наглухо повис и перестал открываться после перезагрузки. Судя по всему пытался отрисовать страницу с большим количеством HTML не преобразованного в маркдаун.
После тщетных попыток c переоткрытиями, запретами скриптов и т.д. были предприняты исследования по результатам которых:

  • база хранится локально в так называемом Web storage
  • в Firefox есть просмоторщик (правда без возможности редактирования) этого Web storage. Редактирование обещают в следующих релизах. Причем под Mozilla есть несколько плагинов, которые позволяют смотреть/редактировать Web storage. Ни один из них так и не заработал, причины были разные. Какой-то уменьшал окно до минимума, какие-то просто висли.
  • При помощи Noscript отключаем скрипты, заходим на https://stackedit.io/
  • Запускаем Storage inspector (Shift-F9, если включен в настройках или открыть панель разработчика и включить в настройках). В Local storage ищем, какие файлы хранятся на компьютере
  • Находим файл, который подвешивает систему, копируем его ключ и вставляем в скрипт очистки
var filename = 'file.q0rBqPMvChLNtkN55HimybX9';

localStorage.removeItem(filename + '.content');
localStorage.removeItem(filename + '.title');
localStorage.removeItem(filename + '.editorEnd');
localStorage.removeItem(filename + '.editorStart');
localStorage.removeItem(filename + '.publish');
localStorage.removeItem(filename + '.selectTime');
localStorage.removeItem(filename + '.sync');
localStorage.removeItem(filename + '.title');
localStorage.setItem('file.list', localStorage.getItem('file.list').replace(';' + filename + ';', ';'));
console.log(localStorage.getItem('file.list'));
  • Запускаем скрипт в консоли и база очищена

Статья, приведшая к зависанию будет удалена!

Вообще Stackedit отличный сервис для публикаций! Вот только в последее время публикация в Blogger работает раз из пяти

Written with StackEdit.

Технология дня

Отличная статья от Toon Koppelaars с перечнем front-end технологий, которые появились за последние несколько лет. И многие из них, как правильно заметил автор

technologies du-jour: hot today, forgotten tomorrow

Надеюсь, что он прав и нам, разработчикам БД, можно расслабиться и продолжать развиваться в своей области.

2016-07-23

DBLINK и сессии

В ходе исследований по loopback links чуть было не попал впросак с отловом сессий, создаваемых для dblink.
Судя по всему Oracle создает сессию для dblink один раз. В доказательство этого сделаем after logon trigger, который будет собирать информацию о подключениях

SQL> DROP TABLE log_session PURGE;

Table dropped.

SQL> CREATE TABLE log_session(username VARCHAR2(30), conn_time timestamp, info VARCHAR2(4000));

Table created.
SQL> CREATE OR REPLACE TRIGGER ta_connect AFTER logon ON DATABASE
  2  BEGIN
  3    INSERT INTO log_session VALUES (USER, SYSTIMESTAMP, NULL);
  4    COMMIT;
  5  END;
  6  /

Trigger created.

SQL> SHOW ERRORS
No errors.
SQL> CONNECT SYSTEM/manager
Connected.
SQL> SELECT COUNT(*) FROM log_session;

  COUNT(*)
----------
         1

Мы подключились, строка вставилась

SQL> SELECT * FROM dual@loopback;

D
-
X

SQL> SELECT COUNT(*) FROM log_session;

  COUNT(*)
----------
         2

Первое обращение по dblink, создалась сессия

SQL> SELECT * FROM dual@loopback;

D
-
X

SQL> SELECT COUNT(*) FROM log_session;

  COUNT(*)
----------
         2


Второе обращение по dblink – сессия не создалась, используется предыдущая

SQL> SELECT 'Hello' FROM a@loopback WHERE ROWNUM=1;

'HELL
-----
Hello

SQL> SELECT COUNT(*) FROM log_session;

  COUNT(*)
----------
         2


Поменяем таблицу – результат тот же. Новой сессии нет

SQL> CONNECT SYSTEM/manager
Connected.
SQL> SELECT COUNT(*) FROM log_session;

  COUNT(*)
----------
         3


Переподключились, теперь счетчик стал 3

SQL> SELECT * FROM dual@loopback;

D
-
X

SQL> SELECT COUNT(*) FROM log_session;

  COUNT(*)
----------
         4

В свежей сессии для dblink выполняется еще одно подключение

Loopback links, ORA-04091 и dirty read в Oracle

При чтении статьи встретилась знакомая по триггерам ошибка mutating table error в неожиданном месте, интересный способ ее обхода и совершенно неожиданные результаты.

CREATE TABLE a(n NUMBER);
Table created
INSERT INTO a VALUES(100);
1 row inserted
INSERT INTO a VALUES(200);
1 row inserted
INSERT INTO a VALUES(300);
1 row inserted
COMMIT;
Commit complete
CREATE OR REPLACE FUNCTION a_avg RETURN NUMBER AS
  l_avg NUMBER;
BEGIN
  SELECT AVG(n) INTO l_avg FROM a;
  dbms_output.put_line('avg=' || l_avg);
  RETURN l_avg;
END a_avg;
/
Function created
SHOW ERRORS
No errors for FUNCTION SYSTEM.A_AVG
UPDATE a SET n = a_avg();
UPDATE a SET n = a_avg()
ORA-04091: table SYSTEM.A is mutating, trigger/function may not see it
ORA-06512: at "SYSTEM.A_AVG", line 4

Мы создали простую таблицу и функцию, которая считает среднее значение по этой таблице. С помощью функции мы попробуем усреднить все значения.
В результате мы получаем ORA-04091 без каких-либо триггеров

Далее в статье приводится способ обхода через loopback database link. Модифицируем немного функцию и вставляем @loopback

CREATE OR REPLACE FUNCTION a_avg_loopback RETURN NUMBER AS
  l_avg NUMBER;
BEGIN
  SELECT AVG(n) INTO l_avg FROM a@loopback;
  dbms_output.put_line('avg=' || l_avg);
  RETURN l_avg;
END a_avg_loopback;
/
Function created
SHOW ERRORS
No errors for FUNCTION SYSTEM.A_AVG_LOOPBACK
UPDATE a SET n = a_avg_loopback();
avg=200
avg=233.333333333333333333333333333333333333
avg=244.444444444444444444444444444444444444
3 rows updated
ROLLBACK;
Rollback complete

Ошибка пропала, но… Через loopback мы смогли увидеть данные, которые еще не закоммичены. Этакий dirty read, но скорее всего мы просто присоединяемся к той же транзакции. Причем, как видно, результат для каждой строчки считается с учетом обновленных строк.

UPD: дальнейшее исследование показало, что loopback link вообще не создал отдельной сессии.

UPD2: сессия создается, но ровно 1 раз за сессию. Исследование тут

Классификация ограничений

Наткнулся на очень интересную статью адепта триггеров о констрейнтах.
Классификация вышла такая:

  • Статические ограничения
    • на атрибут – salary > 10000
    • на группу атрибутов – усы могут быть только у мужчин
    • табличные – проверяется несколько строчек таблицы, например уникальные ключи
    • базы данных – проверяются несколько таблиц, например сумма заказа не превышает остатка по счету клиента
  • Динамические ограничения – те, что невозможно проверить по снапшоту базы данных (т.е. запросом или несколькими запросами), они связаны с изменением во времени. Например по каждому документу в течении рабочего дня должен быть выписан акт.

Такие классификации полезны для построения однозначных архитектур, т.к. формализовав правила реализации каждого из видов мы не получим всего многообразия методов :)

2016-07-17

Parallel merge

Немножко о parallel merge в упрощенном виде. Для того, что бы часть insert/update шла параллельно необходимо:
1. alter session enable parallel dml;
2. указывать в хинте таблицу в которую мержим

merge /*+ parallel(t1) */ into t1
USING (select c1, c2 from t2) t2
on (t1.c1 = t2.c1)
when matched then
         update set t1.c2 = t1.c2
when not matched then
INSERT(c1, c2) values(t2.c1, t2.c2)

Взято отсюда
К сожалению проблема с прода, когда параллельность не возникала на стенде не воспроизвелась.