Запрет пользователям работать с одной и той же строкой

У меня на работе есть веб-приложение, похожее на систему работы с билетами. Некоторые пользователи вводят новые вопросы. Другие работники выбирают и решают вопросы. Все данные хранятся в MS SQL Server 2005.

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

Чтобы решить эту проблему, я сделал две вещи. Во-первых, gridview, отображающий проблемы для выбора, использует таймер AJAX для обновления каждую секунду. Как только проблема выбрана, она исчезает максимум через одну секунду. Если они выбирают один в течение этой секунды, они получают сообщение с просьбой выбрать другой.

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

Должен быть лучший способ, но я не могу его найти. Есть ли у кого-нибудь опыт работы с подобной ситуацией или есть предложения, чтобы несколько пользователей не выбирали одну и ту же запись для сохранения? Я действительно не хочу полностью отключать часть AJAX, потому что я чувствую, что само по себе сообщение затруднит использование приложения.

Спасибо,


person K Richard    schedule 27.10.2008    source источник


Ответы (6)


Две вещи могут помочь смягчить вашу проблему.

Во-первых, уведомление после выбора о том, что дело было принято, необходимо независимо от ваших временных рамок обновления ajax. Даже проверка каждую секунду не означает, что два человека не могут щелкнуть одно и то же дело в одно и то же время. В таких случаях один из пользователей должен быть уведомлен о том, что его выбор недействителен, даже если он казался действительным при выборе. Это уведомление не должно быть сложным; поддержание легкого, полезного тона может улучшить восприятие пользователя даже в свете разочарования. И если вы идентифицируете пользователя, который уже выбрал эту запись, это не только поможет вашим пользователям координировать свои действия в будущем, но и отвлечет внимание от вашей программы на пользователя, который скрыл пикантный случай. (действительно, руководству может понравиться предоставление вашим пользователям случайных коллизий, поскольку это будет мотивировать их быстрее выбирать дела)

Во-вторых, небольшая настройка того, как вы отображаете свои дела, может уменьшить коллизии выбора. Добавление случайного элемента для порядка отображения и/или фильтрация каждого другого отображаемого случая поможет вашим пользователям естественным образом выбирать разные случаи. Распознавание человеческих образов и выбор задач на самом деле не случайны, поэтому небольшие изменения в представлении могут равняться большим изменениям в поведении выбора. Уменьшение вероятности столкновения делает ваши уведомления о столкновениях редкими (и, следовательно, менее раздражающими для ваших пользователей). Это еще лучше, если ваших пользователей можно разделить на классификации, которые могут помочь определить полезный порядок/фильтрацию случаев.

Хорошо, третья вещь, которая поможет вам со временем, — это если вы будете вести журнал, когда происходят столкновения (с полезными метаданными о столкновении, например, кто был вовлечен и время выбора). Вооружившись достоверными данными о столкновениях, вы сможете определить, что работает, а что нет. Со временем вы сможете отточить свое приложение до реальных вариантов использования, а также заранее выявить потенциальные проблемы. Ничто так не успокаивает пользователей, как понимание проблемы (и возможность объяснить свои планы по ее решению) еще до того, как они узнают о ее существовании.

С помощью этих смягчающих шаблонов вы, вероятно, обнаружите, что можете безопасно сократить время запроса ajax, не влияя на работу пользователей. А с полезным ведением журнала вы будете уверены, что любые настройки, которые вы вносите, действительно работают (или нет, что, возможно, еще полезнее знать).

person Jacob Proffitt    schedule 23.09.2010
comment
+1 за рандомизацию, именно то, что я собирался предложить! - person Ed Daniel; 24.09.2010
comment
Случайный порядок не работает. Билеты имеют приоритет по истечении уровня обслуживания. - person K Richard; 27.09.2010
comment
На чем основано истечение срока действия уровня обслуживания? Это определение, основанное на времени? В зависимости от количества ваших проблем вы можете сгруппировать их с точностью до минуты (или пятнадцати минут), и это будет достаточно хорошо. В любом случае, я знаю, что просьба об исключении из расстановки приоритетов может быть политически проблематичной, но если вы можете, это может оказаться оправданным. - person Jacob Proffitt; 28.09.2010
comment
Когда вопросы вводятся, несколько полей определяют уровень приоритета. Каждый из этих уровней имеет правила для расчета времени обслуживания — окончание работы, два рабочих часа, 48 часов и т. д. По сути, мы применяем это и сохраняем дату и время, рассчитанные с помощью записи. Поскольку представление сетки обновляется каждую секунду, чтобы удалить строки, выбранные пользователями, я сохраняю поле, которое в основном называется Now() — SeriveDeadline. Обновление AJAX придает ему вид обратного отсчета. Команда измеряется процентом обслуживания, поэтому для них важно уделять пристальное внимание оставшемуся времени. - person K Richard; 28.09.2010
comment
Да, это то, с чем вы не хотите связываться. - person Jacob Proffitt; 29.09.2010

Поместите поле метки времени блокировки в строку в базе данных. Напишите хранимую процедуру, которая возвращает true или false, если срок действия timsetamp старше определенного времени. Установите срок действия ваших сеансов в веб-приложении в одно и то же время, через минуту или две. Когда пользователь выбирает строку, он попадает в сохраненный процесс, который помогает приложению решить, следует ли разрешить пользователю изменять его.

Надеюсь, это имеет смысл....

person craigmoliver    schedule 27.10.2008
comment
У меня не так много опыта с вашим методом, но я думаю, что понимаю его. Будут ли пользователи потенциально пытаться изменить записи, которые уже были выбраны? Что я действительно хотел бы сделать, если это возможно, так это не допустить их беспокойства путем выбора недоступных записей. - person K Richard; 27.10.2008
comment
Ну и как их скрыть, если постбэка на сервер не было? - person K Richard; 29.10.2008
comment
Если вы проверите отметку времени и выполните обновление SQL в двух разных транзакциях, которые не являются атомарными друг для друга, это состояние гонки... Хотя я предполагаю, что это, вероятно, сработает с чем-то вроде UPDATE ... WHERE Timestamp < CurrentTimeStamp .... - person asveikau; 25.09.2010

Я сделал что-то подобное, когда пользователь открыл тикет (строку), он назначил этот тикет этому пользователю и установил значение в этой записи, например, и FK для этого конкретного пользователя, поэтому, если кто-то еще пытался открыть этот тикет (строку), он сообщит им, что он уже был назначен кому-то другому.

person Slee    schedule 27.10.2008
comment
На самом деле это часть того, что мы делаем сейчас. Мы меняем код состояния проблемы, чтобы указать, что она поддерживается. Мы постоянно обновляем отметку времени, которая сохраняет запись в этом состоянии, пока она обновляется. Уже одно это заставило бы пользователей выбирать записи, с которыми они не могут работать. - person K Richard; 31.10.2008

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

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

person Daniel Auger    schedule 27.10.2008
comment
К сожалению, из-за характера работы ограничение до следующего в очереди невозможно. Мне нравится идея, что он исчезнет, ​​когда пользователь нажмет на него, если он уже был выбран, но я беспокоюсь, что это только разочарует пользователей. - person K Richard; 27.10.2008
comment
Я думаю, что возможное разочарование пользователей является очень серьезной проблемой. Если вы пойдете по этому пути, вам определенно захочется создать прототип для принятия пользователями. Я думаю, что вы определенно находитесь в затруднительном положении с параллелизмом и удобством использования. - person Daniel Auger; 27.10.2008
comment
Спасибо за ваш отзыв! Вы знаете, когда я впервые начал работать с AJAX, я подумал: наконец, это позволит всем пользователям работать синхронно с данными. Я не совсем разочарован, но я определенно выборочно забыл, как производительность будет учитываться. - person K Richard; 27.10.2008

Пробовали ли вы увеличить время между обновлениями. Я ожидаю, что одного раза в 30 секунд будет достаточно. 40 запросов в минуту — это намного меньше нагрузки, чем 1200 в минуту. Ваши пользователи могут даже не заметить разницы.

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

person tvanfosson    schedule 27.10.2008
comment
Я пришел к выводу, что AJAX, используемый таким образом, вообще не является хорошим решением - будь то каждую секунду или каждые 30 секунд. Обновление — хорошая идея. Я не думал об этом. Небольшая ссылка над gridview будет работать хорошо. Тем не менее, он теряет часть своей полезности. - person K Richard; 27.10.2008
comment
Обычно я использую обновление, чтобы уведомить пользователей о добавлении новой информации, когда они какое-то время не пользовались навигацией. Хотя у меня частота обновления больше 5 минут. - person tvanfosson; 27.10.2008
comment
Поправьте меня, если я что-то упустил, но это похоже на пропаганду решения проблемы параллелизма с помощью вызова sleep() вместо использования блокировки. Это не решит проблему одновременных правок, просто замаскирует ее. - person asveikau; 25.09.2010
comment
@asveikau - я не предлагаю вам решать проблемы параллелизма базы данных, уменьшая время между обновлениями. Я предполагаю, что выполнение меньшего количества проверок может сделать приложение и базу данных более отзывчивыми, когда вы выполняете проверку. Вам по-прежнему нужно обрабатывать параллелизм, т. Е. Не позволять двум людям выбирать одну и ту же проблему, но это должно обрабатываться с использованием транзакции и временных меток при выборе задачи. При менее частых обновлениях, возможно, будет более вероятно, что два человека выберут одну и ту же проблему, но, по крайней мере, проверка и сообщение будут более быстрыми, поскольку запросов будет меньше. - person tvanfosson; 26.09.2010

Я не вижу проблемы, особенно после того, как вы упомянули, что уже помечаете заявки как находящиеся в процессе/поддерживаемом состоянии и имеете отметку времени/версию элемента.

Разве недостаточно следующего:

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

Когда элементы извлекаются в 1, вы включаете метку времени/версию. Когда происходит 2, вы используете оптимистичный подход параллелизма, чтобы убедиться, что если 2 человека попытаются обновить билет одновременно, только первый из них будет успешным.

Что произойдет, так это то, что для второго человека обновление ... где ... временная метка = @timestamp не найдет никаких записей для обновления, и вы сообщите, что билет уже был принят.

Если вы хотите, вы можете использовать вышеперечисленное, чтобы обновлять пользовательский интерфейс по мере захвата билетов. Это может быть просто полное обновление текущей страницы билетов через x раз (возможно, предупреждение/подсказка пользователю) или даже получение списка билетов, измененных для страницы билетов, отображаемой с помощью ajax. У вас все еще есть предыдущие шаги, так как эта модификация предназначена только для удобства пользователей.

person eglasius    schedule 24.09.2010
comment
Все то, что вы описали, происходит сейчас. Проблема в том, что обновление gridview с помощью AJAX неэффективно. Если я установлю таймер обновления на более длительный интервал, пользователи начнут выбирать одну и ту же запись. Оптимистичный параллелизм держит данные под контролем, но тратит время пользователя. Вы должны представить список из 300 элементов, отсортированных по приоритету, и до 20 пользователей, выбирающих лучший. - person K Richard; 28.09.2010