Index: src/wp-includes/taxonomy.php
===================================================================
--- src/wp-includes/taxonomy.php	(revision 27100)
+++ src/wp-includes/taxonomy.php	(working copy)
@@ -1876,6 +1876,7 @@
 		do_action( 'edit_term_taxonomies', $edit_tt_ids );
 		$wpdb->update( $wpdb->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id) + compact( 'taxonomy' ) );
 		do_action( 'edited_term_taxonomies', $edit_tt_ids );
+		set_taxonomy_last_changed( $taxonomy );
 	}
 
 	$objects = $wpdb->get_col( $wpdb->prepare( "SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $tt_id ) );
@@ -1910,6 +1911,7 @@
 		$wpdb->delete( $wpdb->terms, array( 'term_id' => $term ) );
 
 	clean_term_cache($term, $taxonomy);
+	set_taxonomy_last_changed( $taxonomy );
 
 	do_action( 'delete_term', $term, $tt_id, $taxonomy, $deleted_term );
 	do_action( "delete_$taxonomy", $term, $tt_id, $deleted_term );
@@ -2162,6 +2164,7 @@
 			do_action( 'edit_terms', $alias->term_id, $taxonomy );
 			$wpdb->update($wpdb->terms, compact('term_group'), array('term_id' => $alias->term_id) );
 			do_action( 'edited_terms', $alias->term_id, $taxonomy );
+			set_taxonomy_last_changed( $taxonomy );
 		}
 	}
 
@@ -2223,6 +2226,7 @@
 	$term_id = apply_filters('term_id_filter', $term_id, $tt_id);
 
 	clean_term_cache($term_id, $taxonomy);
+	set_taxonomy_last_changed( $taxonomy );
 
 	do_action("created_term", $term_id, $tt_id, $taxonomy);
 	do_action("created_$taxonomy", $term_id, $tt_id);
@@ -2329,6 +2333,7 @@
 	}
 
 	wp_cache_delete( $object_id, $taxonomy . '_relationships' );
+	set_taxonomy_last_changed( $taxonomy );
 
 	do_action('set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids);
 	return $tt_ids;
@@ -2407,6 +2412,7 @@
 		$deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
 		do_action( 'deleted_term_relationships', $object_id, $tt_ids );
 		wp_update_term_count( $tt_ids, $taxonomy );
+		set_taxonomy_last_changed( $taxonomy );
 
 		return (bool) $deleted;
 	}
@@ -2565,6 +2571,7 @@
 			do_action( 'edit_terms', $alias->term_id, $taxonomy );
 			$wpdb->update( $wpdb->terms, compact('term_group'), array( 'term_id' => $alias->term_id ) );
 			do_action( 'edited_terms', $alias->term_id, $taxonomy );
+			set_taxonomy_last_changed( $taxonomy );
 		}
 	}
 
@@ -2600,6 +2607,7 @@
 	$term_id = apply_filters('term_id_filter', $term_id, $tt_id);
 
 	clean_term_cache($term_id, $taxonomy);
+	set_taxonomy_last_changed( $taxonomy );
 
 	do_action("edited_term", $term_id, $tt_id, $taxonomy);
 	do_action("edited_$taxonomy", $term_id, $tt_id);
@@ -2738,9 +2746,12 @@
 
 	$taxonomies = get_object_taxonomies( $object_type );
 
-	foreach ( $object_ids as $id )
-		foreach ( $taxonomies as $taxonomy )
+	foreach ( $object_ids as $id ) {
+		foreach ( $taxonomies as $taxonomy ) {
 			wp_cache_delete($id, "{$taxonomy}_relationships");
+			set_taxonomy_last_changed( $taxonomy );
+		}
+	}
 
 	do_action('clean_object_term_cache', $object_ids, $object_type);
 }
@@ -2800,6 +2811,7 @@
 		}
 
 		do_action('clean_term_cache', $ids, $taxonomy);
+		set_taxonomy_last_changed( $taxonomy );
 	}
 
 	wp_cache_set( 'last_changed', microtime(), 'terms' );
@@ -2819,6 +2831,9 @@
  * @return bool|array Empty array if $terms found, but not $taxonomy. False if nothing is in cache for $taxonomy and $id.
  */
 function get_object_term_cache($id, $taxonomy) {
+	if ( ! post_taxonomy_is_fresh( $id, $taxonomy ) ) {
+		return false;
+	}
 	$cache = wp_cache_get($id, "{$taxonomy}_relationships");
 	return $cache;
 }
@@ -2933,11 +2948,13 @@
 function _get_term_hierarchy($taxonomy) {
 	if ( !is_taxonomy_hierarchical($taxonomy) )
 		return array();
-	$children = get_option("{$taxonomy}_children");
+	$children = false;
+	if ( taxonomy_hierarchy_is_fresh( $taxonomy ) ) {
+		$children = get_option( "{$taxonomy}_children" );
+	}
 
 	if ( is_array($children) )
 		return $children;
-	$children = array();
 	$terms = get_terms($taxonomy, array('get' => 'all', 'orderby' => 'id', 'fields' => 'id=>parent'));
 	foreach ( $terms as $term_id => $parent ) {
 		if ( $parent > 0 )
@@ -3117,6 +3134,7 @@
 		$wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
 		do_action( 'edited_term_taxonomy', $term, $taxonomy );
 	}
+	set_taxonomy_last_changed( $taxonomy->name );
 }
 
 /**
@@ -3142,6 +3160,7 @@
 		$wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
 		do_action( 'edited_term_taxonomy', $term, $taxonomy );
 	}
+	set_taxonomy_last_changed( $taxonomy->name );
 }
 
 /**
@@ -3469,3 +3488,74 @@
 
 	return $parent;
 }
+
+/**
+ * Retrieve the 'last_changed' value for the passed taxonomy. Retrieves
+ *  from cache, if present
+ *
+ * @since 3.9.0
+ *
+ * @param string $taxonomy
+ * @return int
+ */
+function get_taxonomy_last_changed( $taxonomy ) {
+	$last_changed = wp_cache_get( 'last_changed', $taxonomy );
+	if ( ! $last_changed ) {
+		$last_changed = microtime();
+		wp_cache_set( 'last_changed', $last_changed, $taxonomy );
+	}
+	return $last_changed;
+}
+
+/**
+ * Reset 'last_changed' for the passed taxonomy
+ *
+ * @since 3.9.0
+ *
+ * @param string $taxonomy
+ * @return int
+ */
+function set_taxonomy_last_changed( $taxonomy ) {
+	wp_cache_delete( 'last_changed', $taxonomy );
+	return get_taxonomy_last_changed( $taxonomy );
+}
+
+/**
+ * Determine if a post's cache for the passed taxonomy
+ *  is in sync.
+ *
+ * @since 3.9.0
+ *
+ * @param int $id
+ * @param string $taxonomy
+ * @return boolean
+ */
+function post_taxonomy_is_fresh( $id, $taxonomy ) {
+	$last_changed = get_taxonomy_last_changed( $taxonomy );
+	$post_last_changed = wp_cache_get( $id, $taxonomy . '_last_changed' );
+	if ( ! $post_last_changed || $last_changed !== $post_last_changed ) {
+		wp_cache_set( $id, $last_changed, $taxonomy . '_last_changed' );
+		return false;
+	}
+	return true;
+}
+
+/**
+ * Determine if a hierarchy's cache for the passed taxonomy
+ *  is in sync.
+ *
+ * @since 3.9.0
+ *
+ * @param int $id
+ * @param string $taxonomy
+ * @return boolean
+ */
+function taxonomy_hierarchy_is_fresh( $taxonomy ) {
+	$last_changed = get_taxonomy_last_changed( $taxonomy );
+	$hierarchy_last_changed = wp_cache_get( 'hierarchy_last_changed', $taxonomy );
+	if ( ! $hierarchy_last_changed || $last_changed !== $hierarchy_last_changed ) {
+		wp_cache_set( 'hierarchy_last_changed', $last_changed, $taxonomy );
+		return false;
+	}
+	return true;
+}
\ No newline at end of file
Index: tests/phpunit/tests/term.php
===================================================================
--- tests/phpunit/tests/term.php	(revision 27100)
+++ tests/phpunit/tests/term.php	(working copy)
@@ -568,9 +568,47 @@
 		$this->assertEquals( 0, $count );
 	}
 
-	/**
-	 * @ticket 22526
-	 */
+	function test_get_taxonomy_last_changed() {
+		$last_changed = get_taxonomy_last_changed( 'category' );
+		$last_changed_cache = wp_cache_get( 'last_changed', 'category' );
+		$this->assertEquals( $last_changed, $last_changed_cache );
+		wp_cache_delete( 'last_changed', 'category' );
+		$this->assertEquals( $last_changed, $last_changed_cache );
+		$last_changed = get_taxonomy_last_changed( 'category' );
+		$this->assertNotEquals( $last_changed, $last_changed_cache );
+
+		$last_changed2 = get_taxonomy_last_changed( 'category' );
+		$this->factory->category->create();
+		$last_changed3 = get_taxonomy_last_changed( 'category' );
+		$this->assertNotEquals( $last_changed2, $last_changed3 );
+	}
+
+	function test_set_taxonomy_last_changed() {
+		$last_changed1 = set_taxonomy_last_changed( 'category' );
+		$last_changed2 = set_taxonomy_last_changed( 'category' );
+		$this->assertNotEquals( $last_changed1, $last_changed2 );
+
+		$last_changed3 = set_taxonomy_last_changed( 'category' );
+		$last_changed4 = get_taxonomy_last_changed( 'category' );
+		$this->assertEquals( $last_changed3, $last_changed4 );
+	}
+
+	function test_post_taxonomy_is_fresh() {
+		$post_id = $this->factory->post->create();
+		$term_id = $this->factory->category->create( array( 'name' => 'Foo' ) );
+		wp_set_post_categories( $post_id, $term_id );
+
+		$this->assertFalse( post_taxonomy_is_fresh( $post_id, 'category' ) );
+		$this->assertTrue( post_taxonomy_is_fresh( $post_id, 'category' ) );
+		$this->assertTrue( post_taxonomy_is_fresh( $post_id, 'category' ) );
+
+		wp_update_term( $term_id, 'category', array( 'name' => 'Bar' ) );
+
+		$this->assertFalse( post_taxonomy_is_fresh( $post_id, 'category' ) );
+		get_the_category( $post_id );
+		$this->assertTrue( post_taxonomy_is_fresh( $post_id, 'category' ) );
+	}
+
 	function test_category_name_change() {
 		$term = $this->factory->category->create_and_get( array( 'name' => 'Foo' ) );
 		$post_id = $this->factory->post->create();
@@ -584,4 +622,49 @@
 		$cats2 = get_the_category( $post->ID );
 		$this->assertNotEquals( $term->name, reset( $cats2 )->name );
 	}
+
+	function test_hierachy_invalidation() {
+		$tax = 'burrito';
+		register_taxonomy( $tax, 'post', array( 'hierarchical' => true ) );
+		$this->assertTrue( get_taxonomy( $tax )->hierarchical );
+
+		$step = 1;
+		$parent_id = 0;
+		$children = 0;
+
+		foreach ( range( 1, 99 ) as $i ) {
+			switch ( $step ) {
+			case 1:
+				$parent = wp_insert_term( 'Parent' . $i, $tax );
+				$parent_id = $parent['term_id'];
+				break;
+			case 2:
+				$parent = wp_insert_term( 'Child' . $i, $tax, array( 'parent' => $parent_id ) );
+				$parent_id = $parent['term_id'];
+				$children++;
+				break;
+			case 3:
+				wp_insert_term( 'Grandchild' . $i, $tax, array( 'parent' => $parent_id ) );
+				$parent_id = 0;
+				$children++;
+				break;
+			}
+
+			$terms = get_terms( $tax, array( 'hide_empty' => false ) );
+			$this->assertEquals( $i, count( $terms ) );
+			if ( 1 < $i ) {
+				$hierarchy = _get_term_hierarchy( $tax );
+				$this->assertNotEmpty( $hierarchy );
+				$this->assertEquals( $children, count( $hierarchy, COUNT_RECURSIVE ) - count( $hierarchy ) );
+			}
+
+			if ( $i % 3 === 0 ) {
+				$step = 1;
+			} else {
+				$step++;
+			}
+		}
+
+		_unregister_taxonomy( $tax );
+	}
 }
