Обновить
61

Пользователь

1,5
Рейтинг
16
Подписчики
Отправить сообщение
Вам и вам предлагается один и тот же стимул.

Это абстрактная ситуация. На практике такое сложно представить. Я, печатаю это сообщение, долбя по клавишам ноута. А Я, сидящий в метре от ноута уже не способен дотянуться до клавиш, и занимается совсем другими делами. :) Эта хабра-страничка, создается одним и тем же алгоритмом, но выглядит по-разному для каждого пользователя, из-за одного мелкого нюанса — куки. Ну и т.п.
У них все правильно написано: 4 nginx'а (фронтенда) кормят запросами 1 сервер торнадо (бекенд). Дело в том, что «hello, world» отдается бекендом гораздо быстрее, чем форнтенд справляется с обслуживанием подключений клиентов. Поэтому, в одиночку, фронтенд не способен нагрузить бекенд полностью. Хотя на 4-х ядрах логичнее было бы пускать 3+1.
Торнадо стоит использовать в тех случаях, когда есть возможность создания неблокирующихся обработчиков запросов. Чтиво по этой теме: twistedmatrix.com/projects/core/documentation/howto/async.html
У приложений Django иная модель обработки запросов.
Тут говорится о возможности запускать приложение tornado при помощи стороннего сервера wsgi (вместо встроенного). Но при этом теряются асинхронные фичи. Т.е. речь идет о том, что приложение торнадо может быть wsgi _клиентом_.

Джангу под торнадо вы не запустите. Торнадо не является wsgi сервером.
NS славится чертовски легкими select'ами, с поиском по эффективному ключу. NI добавляет легкие вставки.

В предлагаемом (защищаемом? ))) Вами методе:
* для select необходим тяжелый inner join таблицы иерархии самой на себя.
* присутствует бОльшая избыточность. Хотя программно используется лишь один запрос на вставку, база вставляет несколько записей (для которых нужно создать индекс и все это писать на диск).
* Если в колонке таблицы многократно дублируются одни и те же значения, то индексы теряют эффективность.
Все это вместе делает способ представления данных не эффективным.
Не ясно, чем задается прядок узлов в дереве.
Про дополнительную таблицу, простите, не понял…
Тяжелые вставки. Не говоря уже о перемещениях (хотя, фиг с ними, с перемещениями, это не часто нужно).
В классических netsted sets вставка узла заставляет пересчитывать все правое поддерево. Достаточно даже небольшого количества одновременных запросов пользователей на добавление узла, чтобы база просела. Я имею ввиду, например, комменты.
А еще есть Netsted Intervals, у которых этих недостатков меньше, благодаря тому, что left и right задаются рациональными числами. Статья:
www.dbazine.com/oracle/or-articles/tropashko4, математика: arxiv.org/abs/0806.3115v1
Можно багрепорт? :)
  """Использовать регулярку не хотелось бы для одного поиска"""
    translated_text=answer[index:answer.find("</div", index)]
    translated_text=replace_html_mnemonics(translated_text)
    translated_text=translated_text.decode("koi8-r")

А почему koi8-r? В answer есть указание на кодировку страницы в виде.
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>

Мне вот такое выдал… Дергайте от charset= и до " и передавайте значение в translated_text.decode(). Хотя идеологически правильнее было бы декодировать сам answer, прежде чем разгребать.
В убунте еще StarDict, умеет переводить, используя google. Нужно приложить вот такой патч:
#! /bin/sh /usr/share/dpatch/dpatch-run
## google.dpatch by  <funca@localhost>
##
## All lines beginning with `## DP:' are a description of the patch.
## DP: No description.

@DPATCH@
diff -urNad stardict-3.0.1~/src/stardict.cpp stardict-3.0.1/src/stardict.cpp
--- stardict-3.0.1~/src/stardict.cpp	2007-11-05 08:39:51.000000000 +0500
+++ stardict-3.0.1/src/stardict.cpp	2009-08-22 23:36:01.000000000 +0600
@@ -1943,7 +1943,7 @@
 			}
 		}
 	} else if (engine_index == 0) {
-		#define GoogleTranslateStartMark "<div id=result_box dir=ltr>"
+		#define GoogleTranslateStartMark "<div id=result_box dir=\"ltr\">"
 
 		char *p = g_strstr_len(buffer, buffer_len, GoogleTranslateStartMark);
 		if (p) {

сразу подумалось об обратном — модуле к fuse, монтирующим форумы (phpbb2, скажем) как файловую систему. %)
база не позволяет сделать поле `Path` длиннее 1000 символов (#1071 — Specified key was too long; max key length is 1000 bytes), что значит, что, если у нас средняя длина ID будет 4 символов мы не сможем делать деревья глубже 1000/(4+1), то есть самое глубокое возможное дерево в таком случае — 200 элементов. и 166 при средней длине ключа 5 цифр (если на сайте более 50000 комментариев в среднем)

Materialized path можно строить иначе. В таблице, описывающией иерархию будем хранить ссылку на объект (ElemId) и путь (Path).
ElemId, Path

В каждом сегменте пути будем записывать номер узла в поддереве по порядку (а не ElemId). И сделаем размер сегмента пути фиксированной длины (скажем 3 символа), чтобы сортировка по такому пути выдавала нам дерево в естественном порядке.
"000" -- первый уровень
"000000" -- второй уровень
"000001000"
"000001001"
"000002"
"001"
"002"

Уровень узла в таком дереве вычисляется по формуле Level = strlen(path) / 3.

Сейчас в каждом поддере можно создать не более чем 10^3 = 1000 узлов. Но если увеличить основание системы счисления, то емкость дерева возрастет. В php функция base_convert позволяет увеличить базу до 36. Т.е. емкость уже составит 36^3 = 46656. Для комментов этого достаточно. И размер пути получается небольшим. 1000 символов хватит для кодирования 1000/3 = 333 уровней. Ну, а скажем путь к комменту на 20-м уровне составит 20*3 = 60 символов. В MySQL есть аналогичная функция. Поэтому всю математику связанную с пересчетом пути при модификации дерева можно записывать прямо в запросе.

Конкретно под комментарии стратегию создания пути можно еще оптимизировать. Понятно что, чем глубже уровень, тем меньше «ветвистость» комментов. Максимальное число комментов стоит ожидать лишь на первом уровне. Поэтому только у первого сегмента пути оставим длину 3 символа. А остальные сегменты можно сократить и до 2-х. 1296 комментов на подуровне хватит заглаза. Таким образом путь к комменту на 20 уровне будет составлять всего 41 символ.
"000" -- первый уровень
"00000" -- второй уровень (2 символа в сегменте)
"0000100"
"0000101"
"00002"
"001"
"002"

Да, нужно обязательно ограничить длину индекса в таблице MySQL, для поля Path. Это не сильно скажется на производительности, зато позволит прилично сэкономить по памяти.
нитки/процессы vs событийные.
вот что интересно. если приложение чисто вычислительное (рассчитывает координаты сферического коня в вакууме, или отдает «Hello, World!» :) ), то использование ниток или процессов производительности не прибавляет, а даже наоборот. процессор все равно один и лишние переключения контекста ни к чему. другая картина получается, когда приложение часть времени проводит в ожидании (синхронного ввода-вывода, результата sql запроса, или стоит таймаут на пару секунд :) ). в это время процессор не задействован, сервер может обрабатывать другие соединения. картина производительности будет иная.
серверы, использующие событийную модель, как лицензия gpl: заражают ей все приложение. :)
вот любопытно. если разделить transfer rate (клобайт/сек) на request per seconds, то получим килобайт / запрос.
1014,09	/ 3484,67	0,291
754,55	/ 3140,87	0,240
720,04	/ 3262,5	0,221
654,98	/ 1202,8	0,545
639,66	/ 2651,86	0,241
171,23	/ 712,78	0,240

интересно, что у «flup 2 процесса / 5 тредов» эта величина больше чем у других конфигураций в два с небольшим раза. он что, строчку «Hello World!» отдает в юникоде? =)
Вот так, через дом2:
<a id='target'>click me</a>

$('#target').get(0).addEventListener('click', function(e) { window.console.log('test1') }, false)
$('#target').get(0).addEventListener('click', function(e) { window.console.log('test2'); throw "Error1"; }, false)
$('#target').get(0).addEventListener('click', function(e) { window.console.log('test3') }, false)

...
test1
test2
test3
Error!

Логика диспетчеризации запроса для получения свойства с использованием прототипа, примитивна до безобразия:
— Эй, Объект, гони сюда свойство property!
— Хм… а нету… (если есть, был обязан выдать)
— Тогда пусть вернет тот, кто тебя породил, такого урода! (объект бежит просить в словаре у своего конструктора)

Если и у конструктора такого свойства нет, то обычно, на этом все и заканчивается, ибо конструктор конструктора уже не при делах: «вассал моего вассала не мой вассал». Это все, что доступно в JS.

Диспетчеризация запроса по иерархии классов отражает совсем другое отношение: конкретное-общее (класс -> супер-класс) — и логика диспетчеризации запросов тут совершенно иная. Единственное что их роднит с «объект-конструктор», это транзитивное отношение «instanceOf», которое может означать и «объект типа», и «объект класса» (см. диаграмму www.cafepy.com/article/python_types_and_objects/images/relationships_transitivity.png). Но из-за этого «двойного» смысла и возникает путаница, будто одно можно выразить через другое. Это не так (не даром, на диаграмме стрелки выглядят по разному).
Вы пишете obj.property, явно желая получить о объекта «obj», нечто, связанное с именем «property». Но где объект возьмет это «нечто»? В конструкторе? В __dict__? В каком-то из супер-классов? Для этого, в new-классах python используются умопомрачительные правила: www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#id402018 (следующая статья по вашей ссылке :) ). Другие типы (не-new-классы) имеют другие правила. А благодаря «магическим» методам (__getattr__, __setattr__ и т.п.) и вы можете придумать что-то свое. Но, что бы вы там не напридумывали, «снаружи» это все равно будет выглядить как «obj.property». Вот это и есть абстракция доступа.

В Javascript можно реализовать подобные правила, и описать их внутри методов. Но тогда и «снаружи» они будут выглядить как вызовы методов. Мне попадалось, например, такое: value = obj.findName('property').get(). Внутри findName() реализован механизм ресолвинга свойства по иерархии классов, а при помощи get() абстрагирован доступ к value. Т.е. логически все сделано. Но без абстракции доступа (синтаксического сахара, со стороны языка, если хотите).
Вот за что я не люблю jQuery, так это за то как, он относится к пользовательским событиям. Пример:
$().bind('test', function() { window.console.log('test1') })
$().bind('test', function() { window.console.log('test2'); throw 'Error!'; })
$().bind('test', function() { window.console.log('test3') })
$().trigger('test');
...
test1
test2
Error!

'test3' нет. Т.е. если какой-то обработчик обломался и бросил исключение, то обработчики, стоящие в цепочке после него, событие не получат.

C практической точки зрения, события используются в том случае, когда необходимо синхронизировать работу ряда _независимых_ приложений. Поэтому глюки отдельного обработчика не должны влиять на диспетчеризацию события (если только он явно не вызвал event.stopImmediatePropagation()). Это же не поток.

В других фреймворках (том же prototype.js) такого глюка нет.

Информация

В рейтинге
1 912-й
Зарегистрирован
Активность