Лицензирование Windows Server 2012

22:26 Рубрика: Ацкий одмин

С выходом Windows Server 2012 Microsoft серьезно пересмотрела правила лицензирования, с учетом последних тенденций в отрасли. В частности, уделено самое пристальное внимание виртуальным средам, а также существенно изменена продуктовая линейка. Надо сказать, что это пошло только на пользу, схема стала намного проще и понятнее, сохранив при этом общие принципы лицензирования. Самое время познакомиться с предметом более подробно.


Простые решения при *** STOP 0x0000007B

17:48 Рубрика: Ацкий одмин


Достаточно распостранeнный STOP 0x0000007b на ситемах под управлением WIndows XP. Возникает чаще всего при установке операционной системы либо при переносе системы на другую платформу в целом или материнскую плату. В большинстве случаев причиной является драйвер контроллера дисковых устройств.


Removing The Last Exchange 2003 Server From Exchange 2007

17:45 Рубрика: Ацкий одмин

The steps required in order to remove the last Exchange 2003 server from an organization that has been migrated to Exchange 2007.


Accessible HTML5 Video Player

12:09 Рубрика: Web Development

by the PayPal Accessibility Team

See the Authors section below for details.

What is it?

A lightweight HTML5 video player which includes support for captions and screen reader accessibility. For details, read the blog post Introducing an Accessible HTML5 Video Player on the PayPal Engineering blog.


  • Provides an HTML5 video player with custom controls.
  • Supports captions; simply denote a VTT caption file using the standard HTML5 video syntax.
  • Uses native HTML5 form controls for volume (range input) and progress indication (progress element).
  • Accessible to keyboard-only users and screen reader users.
  • Option provided to set captions on or off by default (upon loading).
  • Option provided to set number of seconds by which to rewind and forward.
  • The width adjusts to the width of the video element.
  • No dependencies. Written in "vanilla" JavaScript.
  • When JavaScript is unavailable, the browser's native controls are used.


CSS and Image

Insert the CSS in the Head of your HTML document. You'll also need to upload the sprite image (or use your own) and adjust the path in the CSS file.

<link rel="stylesheet" href="/css/px-video.css" />


Insert the HTML5 video markup in the Body of your HTML document. Replace the video, poster, and caption URLs. Modify the sizes of video and fallback image as needed.

<div class="px-video-container" id="myvid">
    <div class="px-video-img-captions-container">
        <div class="px-video-captions hide" aria-hidden="true"></div>
        <video width="640" height="360" poster="media/foo.jpg" controls>
            <source src="foo.mp4" type="video/mp4" />
            <source src="foo.webm" type="video/webm" />
            <track kind="captions" label="English captions" src="media/foo.vtt" srclang="en" default />
                <a href="foo.mp4">
                    <img src="media/foo.jpg" width="640" height="360" alt="download video" />
    <div class="px-video-controls"></div>


Insert the JavaScript file right before the closing Body element of your HTML document. Add a Script element to initialize the video. Options are passed in JSON format. The options are:

  • videoId: the value of the ID of the widget container (string) [required]
  • captionsOnDefault: denotes whether to show or hide caption upon loading (boolean) [optional, default is true]
  • seekInterval: the number of seconds to rewind and fast forward (whole number) [optional, default is 10]
  • videoTitle: short title of video; used for aria-label attribute on Play button to clarify to screen reader user what will be played (text) [optional, default is "Play"]
  • debug: turn console logs on or off (boolean) [optional, default is false]
<script src="js/px-video.js"></script>
// Initialize
new InitPxVideo({
    "videoId": "myvid",
    "captionsOnDefault": true,
    "seekInterval": 20,
    "videoTitle": "clips of stand-up comedy",
    "debug": true

Live Demo

View Demo

Feedback and Contributions

If you experience any errors or if you have ideas for improvement, please feel free to open an issue or send a pull request.

You can also follow and contact the PayPal Accessibility team on Twitter: @PayPalInclusive


Browser Support

  • Chrome: full support.
  • Safari: full support.
  • Firefox: full support.
  • Internet Explorer 10, 11: full support.
  • Internet Explorer 9: native video player used (aesthetic choice since HTML5 range input and progress element are not supported).
  • Internet Explorer 8: renders fallback content of video element (in the demo, this is an image linked to the video file).
  • Smartphones and tablets: controls and captions are not customized as both are natively supported in latest versions.

Limitations and Known Issues

  • Currently, only one caption file per video is supported.
  • Only VTT caption files are supported (not SRT nor TTML). VTT cue settings are not supported but inline styles function (see first few lines of example).
  • The controls have a minimum width of 360px.

Related Resources

Copyright and License

Copyright 2014, eBay Software Foundation under the BSD license.


Make a debian xbmc box in about an hour

15:51 Рубрика: XBMC

We're making a minimal htpc today kids. Grab your gear and follow along.


  • core2duo 2.6
  • 1GB DDR2
  • 200GB sata drive
  • Geforce 8600gt


  • Minimal install of debian wheezy
  • xbmc run from nodm
  • media files stored on NFS share

Install Debian

First install a minimal debian installation. Pop in a boot stick and run through the installer. Simple stuff. Only thing to note is that we are looking for MINIMAL, so when it asks you which package groups you want, don't select anything (well, maybe ssh).

Install needed packages


aptitude install xserver-xorg-video-nouveau xserver-xorg-video-vesa nodm xorg-common x11-session-utils xinit alsa nfs-common xbmc xbmc-bin xbmc-common xbmc-data xbmc-eventclients-common xbmc-eventclients-dev xbmc-eventclients-j2me xbmc-eventclients-ps3 xbmc-eventclients-wiiremote xbmc-eventclients-xbmc-send xbmc-skin-confluence xbmc-standalone

Configure nodm

Edit nodm config to start on boot, and to run as the correct user

vi /etc/default/nodm
# nodm configuration                                                                                                                                                                
# Set NODM_ENABLED to something different than 'false' to enable nodm                                                                                                               
# User to autologin for                                                                                                                                                             
# First vt to try when looking for free VTs                                                                                                                                         
# X session                                                                                                                                                                         
# Options for the X server                                                                                                                                                          
NODM_X_OPTIONS='-nolisten tcp'                                                                                                                                                      
# If an X session will run for less than this time in seconds, nodm will wait an                                                                                                    
# increasing bit of time before restarting the session.                                                                                                                             

Configure NFS

Test that nfs works

mount /mnt
ls /mnt
umount /mnt

Make a new nfs moutpoint

mkdir /penny
chmod 755 /penny/

Add to fstab

echo "    /penny  nfs     ro      0       0" >> /etc/fstab


mount /penny/
ls /penny

Graphics setup (without touching X configs!)

Run to allow normal users to start X (Might not be needed)

dpkg-reconfigure x11-common

Edit to remove "quiet" and add "nomodeset". For me, this was all that was needed to make tv out work.

vi /etc/default/grub
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)

# Uncomment to disable graphical terminal (grub-pc only)

# The resolution used on graphical terminal
# note that you can use only modes which your graphic card supports via VBE
# you can see them in real GRUB with the command `vbeinfo'

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux

# Uncomment to disable generation of recovery mode menu entries

# Uncomment to get a beep at grub start
GRUB_INIT_TUNE="480 440 1"

Create .xsession for nodm/xbmc user

  su silver
  cd ~
  vi .xsession
chmod +x .xsession

Reboot the box and see if it works!

At this point you should have xbmc up, and nfs mounted. You may now configure xbmc as you see fit.



Мониторинг температуры домашнего сервера

15:41 Рубрика: Linux

Для получения красивого графика температуры в панели администрирования OpenMediaVault нужно проделать вот что

Список Звягинцева

15:40 Рубрика: Всячина

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

Уже не раз приходилось по просьбе разных изданий составлять короткие списки самых выдающихся фильмов, когда-либо снятых во всем мире. Ясное дело, что подобные списки всегда грешат даже не субъективностью, от этого никуда не денешься, а какой-то труднообъяснимой случайностью выбора. Я уже сам не раз замечал, что в одном списке у меня одни приоритеты, в другом – несколько и­ные. Конечно, все крутится вокруг одних и тех же имен, но расстановка их часто меняется, да и нередко в такой список попадают вдруг названия или имена, которых не было в прежних списках, притом, что это не новые какие-то картины, снятые вчера. В общем, странное это дело – расставлять иерархические оценки, поскольку они в сильной зависимости от случая – сегодня вдруг померещилось, что лучший фильм всех времен и народов этот, а уже завтра вдруг тот. Чуть не забыл поделиться еще и следующим наблюдением: иногда, читая чужие пантеоны, вдруг бьешь себя по лбу – как же это я забыл упомянуть! Ну и, конечно же, всегда не хватает количества мест, определенных заказчиком опроса: в списке из десяти фильмов всегда найдется одиннадцатый. Вдобавок, сомневаюсь, что у читателя может возникнуть ошибочное впечатление, что предлагаемая кем-то десятка (пятерка) лучших – это тот пантеон, который избран из всего невероятного многообразия фильмов, снятых более чем за сотню лет. Даже трудно себе вообразить прилежного зрителя, который бы мог похвастать тем, что не упустил ничего. Это просто не в силах человеческих. Вот вам элементарная арифметика. Представим себе, что ежегодно в мире создается в среднем более 7 000 фильмов. А это очень приблизительная цифра и, скорее, заниженная. Помножьте эту цифру на 100, и выйдет примерное количество полнометражных фильмов, снятых за всю историю игрового кино. Одним словом, это океан информации, океан, который ни одна человеческая жизнь не в состоянии переварить. Путем нехитрого подсчета, с огромной долей погрешности, округлив длительности фильмов до одного часа (исключительно для легкости счета), мы можем прийти к простому заключению, что для того, чтобы увидеть все снятые в мире фильмы человеку нужно было бы, отказавшись от сна, еды и прочих занятий, сидеть перед экраном 80 лет. Ясное дело, что общую массу всего этого безбрежного океана заполняет неисчислимое количество часов бездарного, не заслуживающего никакого внимания мусора. Ясное дело, что бесспорные или, скажем так, отмеченные многими шедевры встречались в истории кино не так часто, как этого, возможно, нам хотелось бы, но даже и концентрируясь только на них, невозможно увидеть все. Лично мне трудно похвастать прилежностью и системностью виденного. Всякий раз, когда я попадаю на берегу Черного моря в кинозал моего давнего друга-киномана Андрея Дементьева, коллекция фильмов которого насчитывает более 20 000 наименований, я гляжу на огромную стену стеллажей и бессильно опускаю руки, отчетливо понимая, что даже среди этих полок стоят фильмы, которых я не увижу никогда.

Настоящий список, можно сказать, имеет случайный характер. Строгим в нем является только то, что он целиком сосредоточен на игровом постановочном кино. Повторю, я не считаю себя прилежным зрителем, у меня очень много белых пятен на этой карте. Пятен, которые я по возможности неспешно и помалу заполняю. Список этот был однажды составлен по просьбе студентов в течение нескольких часов, по памяти, без оглядки на справочники и энциклопедии. И потому, конечно же, не исчерпывает всей полноты темы. Я попытался вспомнить все удивительные минуты потрясений, пережитых мною перед экраном и записал эти воспоминания. Когда я удовлетворился, почувствовав, что вычерпал из своей памяти все или почти все счастливые минуты созерцаний, я поставил точку и намеренно впоследствии не подвергал этот список какой бы то ни было редактуре, поскольку думаю, ценность его в том и состоит, что он собран почти безответственно и, именно в виду случайных причин причудливой избирательности нашей памяти, неполон. И последнее: в порядке перечисления имен нет никакой иерархии. То есть, если Жан Виго стоит в списке ниже Одзу или Дзурлини, то это не значит ничего, кроме причуд в работе памяти.



Список имен (смотреть все):

Робер Брессон

Микеланджело Антониони

Андрей Тарковский

Ингмар Бергман

Акира Куросава                                                                    

Эрик Ромер

Братья Дарденны

Алексей Герман


Список фильмов:

Робер Брессон "Приговоренный к смерти бежал", "Дневник сельского священника", "Наудачу, Бальтазар", "Процесс Жанны Д`Арк", "Мушетт"

Микеланджело Антониони "Приключение", "Ночь", "Затмение", "Blowup" ("Фотоувеличение")

Акира Куросава "Семь самураев", "Расёмон", "Телохранитель", "Трон в крови"

Карл Теодор Дреер "Слово", "День гнева"

Луис Бунюэль "Дневная красавица", "Скромное обаяние буржуазии", "Назарин"

Ингмар Бергман "Осенняя соната", "Персона", "Земляничная поляна", "Причастие", "Девичий источник", "Шепоты и крики", "Молчание"

Андрей Тарковский "Андрей Рублев", "Зеркало", "Сталкер", "Жертвоприношение"

Эрик Ромер "Моя ночь у Мод", etc.

Братья Дарденны "Дитя", "Розетта", "Сын"

Джон Кассаветис "Мужья", "Тени", "Лица"

Теренс Малик "Новый Свет", "Дни жатвы", "Тонкая красная линия"

Отар Иоселиани "Листопад", "Жил певчий дрозд", "Пастораль", "И стал свет"

Алексей Герман "Проверка на дорогах", "20 дней без войны", "Мой друг Иван Лапшин"

Джим Джармуш "Вне закона", "Страннее, чем рай", "Мистический поезд"

Стенли Кубрик "Барри Линдон", "Одиссея ХХI века", "Тропы славы"

Вим Вендерс "С течением времени", "Алиса в городах", "Небо над Берлином"

Ларс фон Триер "Рассекая волны", "Догвиль"

Михаэль Ханеке "Пианистка", "Белая лента"

Вонг Кар-Вай "Любовное настроение", "Чунгинкский экспресс"

Мартин Скорсезе "Таксист", "Злые улицы", "Бешеный Бык"

Витторио де Сика "Похитители велосипедов", "Умберто Д."

Вуди Аллен "Интерьеры", "Матч Пойнт", "Манхэттен", "Энни Холл"

Клод Соте "Нелли и господин Арно", "Мелочи жизни"

Дэвид Линч "Синий бархат", "Шоссе в никуда"

Бела Тарр "Сатантанго", etc.

Альфред Хичкок "Психо", "Головокружение", "Птицы"

Федерико Феллини "Сладкая жизнь", "8 1/2"

Бернардо Бертолуччи "Последнее танго в Париже", "Конформист"

Питер Гринуэй "Повар, вор, его жена и ее любовник", "Книги Просперо"

Ясуджиро Одзу "Токийская история" etc.

Чарли Чаплин "Золотая лихорадка"

Андрон Кончаловский "История Аси Клячиной, которая любила, да не вышла замуж, потому что гордая была", "Поезд-беглец"

Валерио Дзурлини "Пустыня Тартари"

Хероси Тесигахара "Женщина в песках"

Питер Брук "Модерато кантабиле"

Луи Маль "Любовники"

Жак Риветт "Прекрасная спорщица"

Сэм Мендес "Красота по-американски"

Такеши Китано "Фейерверк"

Пол Томас Андерсон "Магнолия"

Братья Коэны "Фарго"

Райнер Вернер Фассбиндер "Почему господина Р. охватило безумие"

Карлос Рейгадас "Безмолвный свет"

Геннадий Шпаликов "Долгая счастливая жизнь"

Глеб Панфилов "В огне брода нет"

Александр Медведкин "Счастье"

Жан Виго "Аталанта"

Жан Кокто "Орфей"

Эрмано Ольми "Дерево для башмаков"

Орсон Уэллс "Процесс"

Марко Феррери "Дилинджер мертв"

Пьер Паоло Пазоллини "Теорема"

Питер Богданович "Последний киносеанс"

Жан-Люк Годар "На последнем дыхании"

Франсуа Трюффо "400 ударов"

Фридрих В. Мурнау "Восход солнца"

Энг Ли "Ледяной ветер"

Годфри Реджио "Коянискацци"


Cписок составлен в августе 2010 года.



OpenMediaVault on Debian

15:38 Рубрика: Linux

At the time of writing OpenMediaVault 0.6 is pre-release. But it is possible to install OpenMediaVault on Debian Wheezy in order to get some testing done.

Install Debian Wheezy on your target VM or test server. Go with the defaults until the 'Software selection' dialogue. Make sure everything is unselected, like this:

[ ] Debian desktop environment
[ ] Web server
[ ] Print server
[ ] SQL database
[ ] DNS Server
[ ] File server
[ ] Mail server
[ ] SSH server
[ ] Laptop
[ ] Standard system utilities

After the install is complete, reboot and login to the new Debian system as root.

Update the repository sources and add the contrib and non-free repositories.

nano /etc/apt/sources.list

It should look something like this:

deb http://ftp.uk.debian.org/debian/ wheezy main contrib non-free
deb-src http://ftp.uk.debian.org/debian/ wheezy main contrib non-free

deb http://security.debian.org/ wheezy/updates main contrib non-free
deb-src http://security.debian.org/ wheezy/updates main contrib non-free

# wheezy-updates, previously known as 'volatile'
deb http://ftp.uk.debian.org/debian/ wheezy-updates main contrib non-free
deb-src http://ftp.uk.debian.org/debian/ wheezy-updates main contrib non-free

Now add the OpenMediaVault repository.

echo "deb http://packages.openmediavault.org/public kralizec main" > /etc/apt/sources.list.d/openmediavault.list


apt-get update

Install the OpenMediaVault repository key and Postfix.

apt-get install openmediavault-keyring postfix
  • When the 'Postfix Configuration' dialogue is displayed choose No configuration.

Update again and install OpenMediaVault.

apt-get update
apt-get install openmediavault
  • When the 'Configuring mdadm' dialogue is displayed enter none.
  • Do you want to start MD arrays automatically? YES
  • When the 'ProFTPD configuration' dialogue is displayed choose standalone.

Initialise OpenMediaVault and reboot.


After the reboot you should be able to connect to the OpenMediaVault WebUI and login as admin with the password of openmediavault.

That's it. Get testing.


Производительность сайта на Drupal. Анализ серверной части.

15:08 Рубрика: Drupal

Немного воды

Что делать, если сайт дохнет прямо на глазах? С чего начать, если вам подсунули полуживой проект с просьбой поднять его на ноги? Ответ выглядит немного по-капитански: анализ. Вам надо понять, где именно закралась проблема в производительности, которая мешает быстрой работе сайта. Сразу хочу сказать, что в этой статье я буду принимать на веру, что вы выбрали правильный хостинг, и проблема заключается не в нём. Безусловно, многие проблемы с производительностью на сервере можно решить докупив ещё железа, однако не каждый заказчик готов платить за это (хотя по подсчётам, докупить железа обойдётся гораздо дешевле, чем тратиться на специалиста по производительности, но кому это объяснишь ;)). Однако если же косяк с производительностью серьёзный - то он может съесть ресурсы даже докупленного железа, и тогда на вас очень обидятся. А если проблема окажется в клиентской части сайта - то хоть дата-центры скупайте, а у клиентов сайты будут тормозить.


A Beginner's Guide to Caching Data in Drupal 7

14:27 Рубрика: Drupal

Building complicated, dynamic content in Drupal is easy, but it can come at a price. A lot of the stuff that makes a site engaging can spell 'performance nightmare' under heavy load, thrashing the database to perform complex queries and expensive calculations every time a user looks at a node or loads a particular page.

One solution is to turn on page caching on Drupal's performance options administration page. That speeds things up for anonymous users by caching the output of each page, greatly reducing the number of DB queries needed when they hit the site. That doesn't help with logged in users, however: because page level caching is an all-or-nothing affair, it only works for the standardized, always-the-same view that anonymous users see when they arrive.

Eventually there comes a time when you have to dig in to your code, identify the database access hot spots, and add caching yourself. Fortunately, Drupal's built-in caching APIs and some simple guidelines can make that task easy.

The basics

The first rule of optimization and caching is this: never do something time consuming twice if you can hold onto the results and re-use them. Let's look at a simple example of that principle in action:

function my_module_function() {
$my_data = &drupal_static(__FUNCTION__);
  if (!isset(
$my_data)) {
// Do your expensive calculations here, and populate $my_data
    // with the correct stuff..

The important part to look at in this function is the variable named $my_data; we're initializing it with an odd-looking call to drupal_static(). The drupal_static() function is new to Drupal 7, and provides functions with a temporary "storage bin" for data that should stick around even after they're done executing. drupal_static() will return an empty value the first time we call it, but any changes to the variable will be preserved when the function is called again. That means that our function can check if the variable is already populated, and return it immediately without doing any more work.

This pattern appears all over the place in Drupal -- including important functions like node_load(). Calling node_load() for a particular node ID requires database hits the first time, but the resulting information is kept in a static variable for the duration of the page load. That way, displaying a node once in a list, a second time in a block, and a third time in a list of related links (for example) doesn't require three full trips to the database.

In Drupal 6, these static variables were created using the PHP 'static' keyword rather than the drupal_static() function (see the Drupal 6 version of this article for an example). It was also common to provide a $reset parameter on each function that used this pattern, giving modules that needed the freshest information a way to bypass the caching code. While that approach still works in Drupal 7, drupal_static() allows the process to be centralized. When modules need absolutely fresh data, they can call drupal_static_reset() to clear out any temporarily cached information.

Making it stick: Drupal's cache functions

You might notice that the static variable technique only stores data for the duration of a single page load. For even better performance, it's often possible to cache data in a more permanent fashion...

function my_module_function() {
$my_data = &drupal_static(__FUNCTION__);
  if (!isset(
$my_data)) {
    if (
$cache = cache_get('my_module_data')) {
$my_data = $cache->data;
    else {
// Do your expensive calculations here, and populate $my_data
      // with the correct stuff..
cache_set('my_module_data', $my_data, 'cache');

This version of the function still uses the static variable, but it adds another layer: database caching. Drupal's APIs provide three key functions you'll need to be familiar with: cache_get(), cache_set(), and cache_clear_all(). Let's look at how they're used.

After the initial check of the static variable, this function looks in Drupal's cache for data stored with a particular key. If it finds it, $my_data is set to $cache->data and we're done. Combined with the static variable, future calls during this page request won't even need to call cache_get()!

If no cached version is found, the function does the actual work of generating the data. Then it saves it TO the cache so future requests will find it. The key that you pass in as the first parameter can by anything you choose, though it's important to avoid colliding with any other modules' keys. Starting the key with the name of your module is always a good idea.

The end result? A slick little function that saves time whenever it can -- first checking for an in-memory copy of the data, then checking the cache, and finally calculating it from scratch if necessary. You'll see this pattern a lot if you dig into the guts of data-intensive Drupal modules.

Keeping up to date

What happens, though, if the data that you've cached becomes outdated and needs to be recalculated? By default, cached information stays around until some module explicitly calls the cache_clear_all() function, emptying out your record. If your data is updated sporadically, you might consider simply calling cache_clear_all('my_module_data', 'cache') each time you save the changes to it. If you're caching quite a few pieces of data (perhaps versions of a particular block for each role on the site), there's a third 'wildcard' parameter:

('my_module', 'cache', TRUE);

This clears out all the cache values whose keys start with 'my_module'.

If you don't need your cached data to be perfectly up-to-the-second, but you want to keep it reasonably fresh, you can also pass in an expiration date to the cache_set() function. For example:

('my_module_data', $my_data, 'cache', time() + 360);

The final parameter is a unix timestamp value representing the 'expiration date' of the cache data. The easiest way to calculate it is to use the time() function, and add the data's desired lifetime in seconds. Expired entries will be automatically discarded as they pass that date.

Controlling where cached data is stored

You might have noticed that cache_set()'s third parameter is 'cache' -- the name of the table that stores the default cache data. If you're storing large amounts of data in the cache, you can set up your own dedicated cache table and pass its name into the function. That will help keep your cache lookups speedy no matter what other modules are sticking into their own tables. The Views module uses that technique to maintain full control over when its cache data is cleared.

The easiest place to set up a custom cache table is in your module's install file, in the hook_schema() function. It's where all of the custom tables used by your module are defined, and you can even make use of one of Drupal's internal helper functions to simplify the process.

function mymodule_schema() {
$schema['cache_mymodule'] = drupal_get_schema_unprocessed('system', 'cache');

Using the drupal_get_schema_unprocessed() function, the code above retrieves the definition of the System module's standard Cache table, and creates a clone of it named 'cache_mymodule'. Prefixing the name of custom cache tables with the word 'cache' is common practice in Drupal, and helps keep the assorted cache tables organized.

If you're really hoping to squeeze the most out of your server, Drupal also supports the use of alternative caching systems. By changing a single line in your site's settings.php file, you can point it to different implementations of the standard cache_set(), cache_get(), and cache_clear_all() functions. The most popular integration is with the open source memcached project, but other approaches are possible (such as a file-based cache or against PHP's APC). As long as you've used the standard Drupal caching functions, your module's code won't have to be altered.

Advanced caching with renderable content

In Drupal 7, "renderable arrays" are used extensively when building the contents of each page for display. Modules can define page elements like blocks, tables, forms, and even nodes as structured arrays; when the time comes to render the page to HTML, Drupal automatically uses the drupal_render() function to process them, calling the theme layer and other helper functions automatically. Some complex page elements, though, can take quite a bit of time to render into HTML. By adding a special #cache property onto the renderable element, you can instruct the drupal_render() function to cache and reuse the rendered HTML each time the page element is built.

['my_content'] = array(
'#cache' => array(
'cid' => 'my_module_data',
'bin' => 'cache',
'expire' => time() + 360,
// Other element properties go here...

The #cache property contains a list of values that mirror the parameters you would pass to the cache_get() and cache_set() if you were calling them manually. For more information on how caching of renderable elements works, check out the detailed documentation for the drupal_render() function on api.drupal.org.

A few caveats

Like all good things, it's possible to overdo it with caching. Sometimes, it just doesn't make sense -- if you're looking up a single record from a table, saving the result to a database cache is silly. Using the Devel module is a good way to spot the functions where caching will pay off: it can log the queries that are used on your site and highlight the ones that are slow, or the ones that are repeated numerous times on each page.

Other times, the data you're using will just be a bad fit for the standard caching system. If you need to join cached data in SQL queries, for example, cache_set()'s practice of string data as a serialized string will be a problem. In those cases, you'll need to come up with a solution that's specific to your module. VotingAPI maintains one table full of individual votes and another table full of calculated results (averages, sums, etc.) for quick joining when sorting and filtering nodes.

Finally, it's important to remember that the cache is not long term storage! Since other modules can call cache_clear_all() and wipe it out, you should never put something into it if you can't recalculate it again using the original source data.

Go west, young Drupaler!

Congratulations: you now have a powerful set of tools to speed up your code! Go forth, and optimize.

Note: This article is an updated version of an earlier article, and deals specifically with the Drupal 7 API. If you're working with an older version of Drupal, see the Drupal 4 and 5 or Drupal 6 of this article.