Javascript-форум (https://javascript.ru/forum/)
-   Node.JS (https://javascript.ru/forum/node-js-io-js/)
-   -   Синхронная запись в БД (https://javascript.ru/forum/node-js-io-js/73612-sinkhronnaya-zapis-v-bd.html)

stweet 28.04.2018 14:57

Синхронная запись в БД
 
День добрый форумчане! Устал бороться в одиночку с «асинхронностью» сии детища NodeJS. Прибегаю к помощи гуру Promises & Multyrequest.

Ситуация такая:
Допустим, у нас в базе (юзаю Pg + Sequalize) есть табличка с данными такого вида:

ID   TYPE    COUNTER
 1   «one»       1
 2   «two»       1


В модели есть методы decCounterByType(type); & incCounterByType(type); и вроде бы все ни чего пока не попробуешь использовать эти методы в цикле. Цикл же имитирует многочисленное обращение к базе, пример:

for(let i  = 0; i < 1000; i ++) {
	Model.incCounterByType(«one»)
		.then(res => console.log(`${i}, ${type}, ${res.counter}`))
		.catch(console.error);
}


Понимаем да, что вывод будет хаотичен и не каждое обращение получит правильный ответ, т. е. лог будет выглядеть примерно так:

0, «one», 1
3, «one», 2
1, «one», 4
2, «one», 3


Где то на просторах нашел решение, и да, оно работает, блокирует обращение:

let last = Promise.resolve({})
for(let i  = 0; i < 1000; i ++) {
	last  = last .then(() => Model.incCounterByType(«one»))
		.then(res => console.log(`${i}, ${type}, ${res.counter}`))
		.catch(console.error);
}


в этом случае у нас вывод будет уже:

0, «one», 1
1, «one», 2
2, «one», 3
3, «one», 4


и вроде бы эврика, решение есть! Но, под правильным углом со знаниями работы алгоритма Promise, мы понимаем, что если один из Promise не выполнит «resolve», или выполнит «reject» or «throw» все остальные обращения ждет фиаско. Пока остановился на таком варианте но уверен, за такой костыль будут бить ногами:

let last = Promise.resolve({})
for(let i  = 0; i < 1000; i ++) {
	last  = last .then(() => Model.incCounterByType(«one»))
		.then(res => console.log(`${i}, ${type}, ${res.counter}`))
		.catch(Promise.resolve));
}


Цель, добиться корректного обращения к БД и синхронный вывод результатов. А как вы блокируете обращения к БД?

Спасибо за участие в обсуждении!

Nexus 28.04.2018 15:03

Что вы пытаетесь вообще сделать?
Почему не увеличить сразу все значения 1 запросом?

stweet 28.04.2018 15:11

Цитата:

Сообщение от Nexus (Сообщение 484331)
Что вы пытаетесь вообще сделать?
Почему не увеличить сразу все значения 1 запросом?

Как вы себе это представляете? Зашло на сайт 100500 человек, сделали запросы а я их должен копить, и когда надумаю(видимо произвольно) сохраню, так?

Aetae 28.04.2018 15:19

Если каждый юзер посылает запрос при заходе - каждый юзер получает свой ответ. И на момент ответа - этот ответ верен. В чём тогда проблема?
Чисто чтоб "порядок был" и цифры красивенькие?

EmperioAf 28.04.2018 15:21

Async/await в помощь
async function foo() { // вернет Promise<undefined>
    for (let i = 0; i < 1000; i++) {
        try {
            const res = await Model.incCounterByType(«one»)
            console.log(`${i}, ${type}, ${res.counter}`)
        } catch(err) {
            // обработка ошибки
        }
    }
}

Nexus 28.04.2018 15:24

stweet, перечитал, прошу прощение.
Не обратил внимание, что у вас это просто имитация.

C postgresql не работал, она не блокирует строку во время её обновления?

stweet 28.04.2018 15:33

Цитата:

Сообщение от Aetae (Сообщение 484334)
Если каждый юзер посылает запрос при заходе - каждый юзер получает свой ответ. И на момент ответа - этот ответ верен. В чём тогда проблема?
Чисто чтоб "порядок был" и цифры красивенькие?

Скорее, мне важно правильное(надежное) выполнение кода. Что бы не получилось так, пока один юзер пытается поднять счетчик второй пытается его понизить и т.д. А в итоге каждый получает не разбириху, у второго счетчик уже поднялся, а у первого ещё ответ не пришел. Или это приблуды консоли и на рабочем примере все будет в порядке? Мне важно, что бы счетчик надежно работал. Если повторить туже операцию но с созданием записей, вы получите и в базе хаотичный счетчик. т.е.:
record 1 = 1
record 2 = 10
record 3 = 9
record 4 = 11
и т.д.

stweet 28.04.2018 15:34

Цитата:

Сообщение от EmperioAf (Сообщение 484335)
Async/await в помощь
async function foo() { // вернет Promise<undefined>
    for (let i = 0; i < 1000; i++) {
        try {
            const res = await Model.incCounterByType(«one»)
            console.log(`${i}, ${type}, ${res.counter}`)
        } catch(err) {
            // обработка ошибки
        }
    }
}

Вы выполните свой код и посмотрите на результат.

stweet 28.04.2018 15:39

Цитата:

Сообщение от Nexus (Сообщение 484336)
stweet, перечитал, прошу прощение.
Не обратил внимание, что у вас это просто имитация.

C postgresql не работал, она не блокирует строку во время её обновления?

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

Пример такой, есть счетчик, равен "3"
Если одновременно "два" юзера жмякнут на "поднять" оба получают "4". А в базе стоит "5" - !неудачный пример!
Если два юзера одновременно зарегистрируются, счетчик идти у них будет не по порядку. - Скорее я об этом.

EmperioAf 28.04.2018 15:39

let GLOBAL_ITER = 0;

function iter() {
    return new Promise((resolve) => {
        setTimeout(() => { 
            GLOBAL_ITER++;
            resolve(GLOBAL_ITER);
        }, 1e3);
    });
}

async function test() {
    for (let i = 0; i < 5; i++) {
        try {
            const curGlobalIter = await iter();
            console.log(curGlobalIter)
        } catch(err) {
        
        }
    }
}

test().then(() => console.log('done'));

как вы можете понять, я увижу постепенно появившиеся с delay 1с
1
2
3
4
5
done


Часовой пояс GMT +3, время: 16:13.