Это абстрактная ситуация. На практике такое сложно представить. Я, печатаю это сообщение, долбя по клавишам ноута. А Я, сидящий в метре от ноута уже не способен дотянуться до клавиш, и занимается совсем другими делами. :) Эта хабра-страничка, создается одним и тем же алгоритмом, но выглядит по-разному для каждого пользователя, из-за одного мелкого нюанса — куки. Ну и т.п.
У них все правильно написано: 4 nginx'а (фронтенда) кормят запросами 1 сервер торнадо (бекенд). Дело в том, что «hello, world» отдается бекендом гораздо быстрее, чем форнтенд справляется с обслуживанием подключений клиентов. Поэтому, в одиночку, фронтенд не способен нагрузить бекенд полностью. Хотя на 4-х ядрах логичнее было бы пускать 3+1.
Тут говорится о возможности запускать приложение tornado при помощи стороннего сервера wsgi (вместо встроенного). Но при этом теряются асинхронные фичи. Т.е. речь идет о том, что приложение торнадо может быть wsgi _клиентом_.
Джангу под торнадо вы не запустите. Торнадо не является wsgi сервером.
NS славится чертовски легкими select'ами, с поиском по эффективному ключу. NI добавляет легкие вставки.
В предлагаемом (защищаемом? ))) Вами методе:
* для select необходим тяжелый inner join таблицы иерархии самой на себя.
* присутствует бОльшая избыточность. Хотя программно используется лишь один запрос на вставку, база вставляет несколько записей (для которых нужно создать индекс и все это писать на диск).
* Если в колонке таблицы многократно дублируются одни и те же значения, то индексы теряют эффективность.
Все это вместе делает способ представления данных не эффективным.
Не ясно, чем задается прядок узлов в дереве.
Про дополнительную таблицу, простите, не понял…
Тяжелые вставки. Не говоря уже о перемещениях (хотя, фиг с ними, с перемещениями, это не часто нужно).
В классических netsted sets вставка узла заставляет пересчитывать все правое поддерево. Достаточно даже небольшого количества одновременных запросов пользователей на добавление узла, чтобы база просела. Я имею ввиду, например, комменты.
"""Использовать регулярку не хотелось бы для одного поиска"""
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 есть указание на кодировку страницы в виде.
Мне вот такое выдал… Дергайте от charset= и до " и передавайте значение в translated_text.decode(). Хотя идеологически правильнее было бы декодировать сам answer, прежде чем разгребать.
база не позволяет сделать поле `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. Это не сильно скажется на производительности, зато позволит прилично сэкономить по памяти.
вот что интересно. если приложение чисто вычислительное (рассчитывает координаты сферического коня в вакууме, или отдает «Hello, World!» :) ), то использование ниток или процессов производительности не прибавляет, а даже наоборот. процессор все равно один и лишние переключения контекста ни к чему. другая картина получается, когда приложение часть времени проводит в ожидании (синхронного ввода-вывода, результата sql запроса, или стоит таймаут на пару секунд :) ). в это время процессор не задействован, сервер может обрабатывать другие соединения. картина производительности будет иная.
интересно, что у «flup 2 процесса / 5 тредов» эта величина больше чем у других конфигураций в два с небольшим раза. он что, строчку «Hello World!» отдает в юникоде? =)
Логика диспетчеризации запроса для получения свойства с использованием прототипа, примитивна до безобразия:
— Эй, Объект, гони сюда свойство 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) такого глюка нет.
Это абстрактная ситуация. На практике такое сложно представить. Я, печатаю это сообщение, долбя по клавишам ноута. А Я, сидящий в метре от ноута уже не способен дотянуться до клавиш, и занимается совсем другими делами. :) Эта хабра-страничка, создается одним и тем же алгоритмом, но выглядит по-разному для каждого пользователя, из-за одного мелкого нюанса — куки. Ну и т.п.
У приложений Django иная модель обработки запросов.
Джангу под торнадо вы не запустите. Торнадо не является wsgi сервером.
В предлагаемом (защищаемом? ))) Вами методе:
* для select необходим тяжелый inner join таблицы иерархии самой на себя.
* присутствует бОльшая избыточность. Хотя программно используется лишь один запрос на вставку, база вставляет несколько записей (для которых нужно создать индекс и все это писать на диск).
* Если в колонке таблицы многократно дублируются одни и те же значения, то индексы теряют эффективность.
Все это вместе делает способ представления данных не эффективным.
Не ясно, чем задается прядок узлов в дереве.
Про дополнительную таблицу, простите, не понял…
В классических netsted sets вставка узла заставляет пересчитывать все правое поддерево. Достаточно даже небольшого количества одновременных запросов пользователей на добавление узла, чтобы база просела. Я имею ввиду, например, комменты.
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 есть указание на кодировку страницы в виде.
Мне вот такое выдал… Дергайте от charset= и до " и передавайте значение в translated_text.decode(). Хотя идеологически правильнее было бы декодировать сам answer, прежде чем разгребать.
#! /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) {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. Это не сильно скажется на производительности, зато позволит прилично сэкономить по памяти.
интересно, что у «flup 2 процесса / 5 тредов» эта величина больше чем у других конфигураций в два с небольшим раза. он что, строчку «Hello World!» отдает в юникоде? =)
<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). Но из-за этого «двойного» смысла и возникает путаница, будто одно можно выразить через другое. Это не так (не даром, на диаграмме стрелки выглядят по разному).
В Javascript можно реализовать подобные правила, и описать их внутри методов. Но тогда и «снаружи» они будут выглядить как вызовы методов. Мне попадалось, например, такое: value = obj.findName('property').get(). Внутри findName() реализован механизм ресолвинга свойства по иерархии классов, а при помощи get() абстрагирован доступ к value. Т.е. логически все сделано. Но без абстракции доступа (синтаксического сахара, со стороны языка, если хотите).
$().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) такого глюка нет.