Is block weight ignored when setting content in hook_block_list_alter()?
Symptoms: When the block is not loaded from cache, but from the database, the ordering works fine. When loading from cache, the weight is ignored.

Diagnosis: The block module uses SQL to order the blocks:

function _block_load_blocks() {
  global $theme_key;

  $query = db_select('block', 'b');
  $result = $query
    -&gt;fields('b')
    -&gt;condition('b.theme', $theme_key)
    -&gt;condition('b.status', 1)
    -&gt;orderBy('b.region')
    -&gt;orderBy('b.weight')
    -&gt;orderBy('b.module')
    -&gt;addTag('block_load')
    -&gt;addTag('translatable')
    -&gt;execute();

// ...


After that, it ignores the weight value, and relies on the order of php array keys (order in which foreach iterates) when rendering:

function _block_get_renderable_array($list = array()) {
  $weight = 0;
  $build = array();
  foreach ($list as $key =&gt; $block) {
    $build[$key] = $block-&gt;content;
    unset($block-&gt;content);

// ...

    $build[$key] += array(
      '#block' =&gt; $block,
      '#weight' =&gt; ++$weight, // HERE LIES THE MADNESS!
    );
    $build[$key]['#theme_wrappers'][] ='block';
  }
  $build['#sorted'] = TRUE;
  return $build;
}


Strategy: Either hack the blocks module (DON'T) or find a workaround. I looked through the block.module and found (using lots of dpm()) where the order of array keys are being reversed:

function _block_render_blocks($region_blocks) {

// ...      

  foreach ($region_blocks as $key =&gt; $block) {
    // Render the block content if it has not been created already.
    if (!isset($block-&gt;content)) {
      // Erase the block from the static array - we'll put it back if it has
      // content.
      unset($region_blocks[$key]); // THIS IS WHY THE ORDER OF KEYS CHANGE

// ...

      if (isset($block-&gt;content) &amp;&amp; $block-&gt;content) {

// ...

        $region_blocks["{$block-&gt;module}_{$block-&gt;delta}"] = $block; // THIS IS WHY THE ORDER OF KEYS CHANGE
      }
    }
  }

  return $region_blocks;
}


Workaround: Manually sort the render array in a hook_page_alter(&amp;$page) implementation.

function mymodule_page_alter(&amp;$page) {
  if (!empty($page['sidebar_first']['my_block_1'])) {
    $page['sidebar_first']['my_block_1']['#weight'] = -1; // move it to the top
    $page['sidebar_first']['#sorted'] = FALSE; // indicate that it still needs to be sorted before rendering
  }
}


I only have two blocks in sidebar_first, of which the bottom one is cached. So I only need to lift the other block ('my_block1') above the cached block. A more sophisticated approach will be to query the weights from the block table again.

I'm not sure whether I should consider this to be a bug in the block module. It is a shortcoming, but it's only exposed because of my custom caching hack.