diff --git a/README.txt b/README.txt index 1b81b28..cff57d7 100644 --- a/README.txt +++ b/README.txt @@ -96,6 +96,28 @@ Configuration Variables ), ); + As of the mongoDB 1.3 php driver slave_ok has been deprecated and instead + read preferences should be used when dealing with a replica set. These + preferences can be set at a connection or collection level see below for the + collection information. + + $conf['mongodb_connections'] = array( + // Connection name/alias + 'slave' => array( + 'host' => 'slave1', + // Database name + 'db' => 'drupal_default', + 'read_preference' => array( + 'preference' => 'secondaryPreferred', + 'tags' => array( + array('dc' => 'east', 'use' => 'reporting'), + array('dc' => 'west'), + ), + ), + 'connection_options' => array('replicaSet' => 'main'), + ), + ); + #2: mongodb_debug A variable primarily for developers, mongodb_debug causes a collection @@ -189,6 +211,22 @@ the hypothetical connection alias 'logginghost' EXAMPLE: $conf['mongodb_collections'] = array('watchdog' => 'logginghost'); +If you are using the 1.3 drivers you can specify the following connection +options db_connection and read_preference. These options allow to control +collection level mongo read preferences and whether any tags should be used. + +$conf['mongodb_collections'] = array( + 'watchdog' => array( + db_connection => 'logginghost', + 'read_preference' => array( + 'preference' => 'secondaryPreferred', + 'tags' => array( + array('dc' => 'east', 'use' => 'reporting'), + array('dc' => 'west'), + ), + ), + ), +); MODULE-SPECIFIC CONFIGURATION ------------ diff --git a/mongodb.drush.inc b/mongodb.drush.inc index 840d36b..1da24f3 100644 --- a/mongodb.drush.inc +++ b/mongodb.drush.inc @@ -113,7 +113,7 @@ function drush_mongodb_query($alias = 'default', $query = '') { } /** - * Returns the basic shell commando string. + * Returns the basic shell command string. */ function _drush_mongodb_connect($alias) { $connections = variable_get('mongodb_connections', array()); diff --git a/mongodb.module b/mongodb.module index 69cf3b1..8fd8ff9 100644 --- a/mongodb.module +++ b/mongodb.module @@ -31,9 +31,22 @@ function mongodb($alias = 'default') { $db = $connection['db']; if (!isset($mongo_objects[$host][$db])) { try { - $mongo = new Mongo($host, $options); - if (!empty($connection['slave_ok'])) { - $mongo->setSlaveOkay(TRUE); + // Use the 1.3 client if available. + if (class_exists('MongoClient')) { + $mongo = new MongoClient($host, $options); + // Enable read preference and tags if provided. This can also be + // controlled on a per query basis at the cursor level if more control + // is required. + if (!empty($connection['read_preference'])) { + $tags = !empty($connection['read_preference']['tags']) ? $connection['read_preference']['tags'] : array(); + $mongo->setReadPreference($connection['read_preference']['preference'], $tags); + } + } + else { + $mongo = new Mongo($host, $options); + if (!empty($connection['slave_ok'])) { + $mongo->setSlaveOkay(TRUE); + } } $mongo_objects[$host][$db] = $mongo->selectDB($db); $mongo_objects[$host][$db]->connection = $mongo; @@ -61,12 +74,25 @@ function mongodb_collection() { $prefixed = mongodb_collection_name($collection_name); } $collections = variable_get('mongodb_collections', array()); - $alias = isset($collections[$collection_name]) ? $collections[$collection_name] : 'default'; + if (isset($collections[$collection_name])) { + // We might be dealing with an array or string because of need to preserve + // backwards comptability. + $alias = !empty($collections[$collection_name]['db_connection']) ? $collections[$collection_name]['db_connection'] : $collections[$collection_name]; + } + else { + $alias = 'default'; + } // Prefix the collection name for simpletest. It will be in the same DB as the // non-prefixed version so it's enough to prefix after choosing the mongodb // object. $mongodb_object = mongodb($alias); $collection = $mongodb_object->selectCollection(mongodb_collection_name($collection_name)); + // Enable read preference and tags at a collection level if we have 1.3 + // client. + if (!empty($collections[$alias]['read_preference']) && get_class($mongodb_object->connection) == 'MongoClient') { + $tags = !empty($collections[$alias]['read_preference']['tags']) ? $collections[$alias]['read_preference']['tags'] : array(); + $collection->setReadPreference($collections[$alias]['read_preference']['preference'], $tags); + } $collection->connection = $mongodb_object->connection; return variable_get('mongodb_debug', FALSE) ? new mongoDebugCollection($collection) : $collection; } @@ -158,7 +184,12 @@ function mongodb_test_group_finished() { */ function mongodb_set_active_connection($alias, $connection_name = 'default') { // No need to check if the connection is valid as mongodb() does this. - $GLOBALS['conf']['mongodb_collections'][$alias] = $connection_name; + if (!empty($GLOBALS['conf']['mongodb_collections'][$alias]['db_connection'])) { + $GLOBALS['conf']['mongodb_collections'][$alias]['db_connection'] = $connection_name; + } + else { + $GLOBALS['conf']['mongodb_collections'][$alias] = $connection_name; + } } /** diff --git a/mongodb_block/mongodb_block.module b/mongodb_block/mongodb_block.module index cc38270..25c19ab 100644 --- a/mongodb_block/mongodb_block.module +++ b/mongodb_block/mongodb_block.module @@ -300,7 +300,7 @@ function _mongodb_block_rehash($theme) { mongodb_block_save_block($collection, $block); } } - $collection->remove(array('_id' => array('$nin' => $ids))); + $collection->remove(array('_id' => array('$nin' => $ids)), array('safe' => TRUE)); return $blocks; } @@ -314,7 +314,7 @@ function _mongodb_block_rehash($theme) { */ function mongodb_block_save_block($collection, $block) { unset($block['module'], $block['delta'], $block['status']); - $collection->save($block); + $collection->save($block, array('safe' => TRUE)); } /** diff --git a/mongodb_block_ui/mongodb_block_ui.admin.inc b/mongodb_block_ui/mongodb_block_ui.admin.inc index b2fc90f..0508a5c 100644 --- a/mongodb_block_ui/mongodb_block_ui.admin.inc +++ b/mongodb_block_ui/mongodb_block_ui.admin.inc @@ -144,7 +144,7 @@ function mongodb_block_ui_admin_display_form_submit($form, &$form_state) { 'weight' => (int) $block['weight'], 'region' => $block['region'], ); - $collection->update($find, array('$set' => $set), array('upsert' => TRUE)); + $collection->update($find, array('$set' => $set), array('upsert' => TRUE, 'safe' => TRUE)); } } drupal_set_message(t('The block settings have been updated.')); @@ -366,7 +366,7 @@ function mongodb_block_ui_admin_configure_submit($form, &$form_state) { 'title' => $form_state['values']['title'], ); // This way region and weight is preserved. - $collection->update(array('_id' => $id), array('$set' => $customized), array('upsert' => TRUE)); + $collection->update(array('_id' => $id), array('$set' => $customized), array('upsert' => TRUE, 'safe' => TRUE)); // Ensure that custom blocks have their settings saved. // @todo: this should probably be done via hook_block_configure(). @@ -377,7 +377,7 @@ function mongodb_block_ui_admin_configure_submit($form, &$form_state) { 'info' => $form_state['values']['info'], 'title' => $form_state['values']['title'], ); - mongodb_collection('block_custom')->save($block); + mongodb_collection('block_custom')->save($block, array('safe' => TRUE)); } module_invoke($form_state['values']['module'], 'block_save', $form_state['values']['delta'], $form_state['values']); drupal_set_message(t('The block configuration has been saved.')); @@ -463,7 +463,7 @@ function mongodb_block_ui_add_block_form_submit($form, &$form_state) { 'info' => $form_state['values']['info'], 'title' => $form_state['values']['title'], ); - $collection->insert($block); + $collection->insert($block, array('safe' => TRUE)); drupal_set_message(t('The block has been created.')); cache_clear_all(); $theme = $form['#theme_key']; @@ -491,14 +491,14 @@ function mongodb_block_ui_custom_block_delete($form, &$form_state, $theme, $modu */ function mongodb_block_ui_custom_block_delete_submit($form, &$form_state) { // Remove the custom block. - mongodb_collection('block_custom')->remove(array('_id' => $form_state['values']['delta'])); + mongodb_collection('block_custom')->remove(array('_id' => $form_state['values']['delta']), array('safe' => TRUE)); // Also remove customization for every theme. $id = array( 'module' => $form_state['values']['module'], 'delta' => $form_state['values']['delta'], ); foreach (list_themes() as $theme) { - mongodb_collection('block_customized', $theme->name)->remove(array('_id' => $id)); + mongodb_collection('block_customized', $theme->name)->remove(array('_id' => $id), array('safe' => TRUE)); } drupal_set_message(t('The block %name has been removed.', array('%name' => $form_state['values']['info']))); cache_clear_all(); diff --git a/mongodb_block_ui/mongodb_block_ui.module b/mongodb_block_ui/mongodb_block_ui.module index 871050f..a068439 100644 --- a/mongodb_block_ui/mongodb_block_ui.module +++ b/mongodb_block_ui/mongodb_block_ui.module @@ -237,12 +237,7 @@ function mongodb_block_ui_custom_block_form($edit = array()) { * Implements hook_menu_delete(). */ function mongodb_block_ui_menu_delete($menu) { - $collection = mongodb_collection('block_custom'); - - $collection->remove(array( - 'module' => 'menu', - 'delta' => $menu['menu_name'], - )); + $collection->delete(array('module' => 'menu', 'delta' => $menu['menu_name']), array('safe' => TRUE)); } /** diff --git a/mongodb_cache/mongodb_cache.inc b/mongodb_cache/mongodb_cache.inc index e137be6..a3b5be8 100644 --- a/mongodb_cache/mongodb_cache.inc +++ b/mongodb_cache/mongodb_cache.inc @@ -67,7 +67,7 @@ class DrupalMongoDBCache implements DrupalCacheInterface { $find = array('expire' => array('$lte' => $cache_flush, '$ne' => CACHE_PERMANENT)); - $collection->remove($find); + $collection->remove($find, array('w' => 0)); } } @@ -133,7 +133,7 @@ class DrupalMongoDBCache implements DrupalCacheInterface { try { $collection = mongodb_collection($this->bin); - $collection->save($entry); + $collection->save($entry, array('w' => 0)); } catch (Exception $e) { // The database may not be available, so we'll ignore cache_set requests. @@ -162,13 +162,13 @@ class DrupalMongoDBCache implements DrupalCacheInterface { // Clear the cache for everyone, cache_lifetime seconds have // passed since the first request to clear the cache. - $collection->remove(array('expire' => array('$ne' => CACHE_PERMANENT, '$lte' => REQUEST_TIME))); + $collection->remove(array('expire' => array('$ne' => CACHE_PERMANENT, '$lte' => REQUEST_TIME)), array('w' => 0)); variable_set('cache_flush_' . $this->bin, 0); } } else { // No minimum cache lifetime, flush all temporary cache entries now. - $collection->remove(array('expire' => array('$ne' => CACHE_PERMANENT, '$lte' => REQUEST_TIME))); + $collection->remove(array('expire' => array('$ne' => CACHE_PERMANENT, '$lte' => REQUEST_TIME)), array('w' => 0)); } } else { @@ -177,19 +177,19 @@ class DrupalMongoDBCache implements DrupalCacheInterface { $collection->remove(); } else { - $collection->remove(array('cid' => new MongoRegex('/' . preg_quote($cid) . '.*/'))); + $collection->remove(array('cid' => new MongoRegex('/'. preg_quote($cid) .'.*/')), array('w' => 0)); } } elseif (is_array($cid)) { // Delete in chunks when a large array is passed. do { $find = array('cid' => array('$in' => array_map('strval', array_splice($cid, 0, 1000)))); - $collection->remove($find); + $collection->remove($find, array('w' => 0)); } while (count($cid)); } else { - $collection->remove(array('_id' => (string)$cid)); + $collection->remove(array('_id' => (string)$cid), array('w' => 0)); } } } diff --git a/mongodb_field_storage/mongodb_field_storage.module b/mongodb_field_storage/mongodb_field_storage.module index 1be9622..ab6c42b 100644 --- a/mongodb_field_storage/mongodb_field_storage.module +++ b/mongodb_field_storage/mongodb_field_storage.module @@ -247,13 +247,9 @@ function _mongodb_field_storage_value($type, $value) { function mongodb_field_storage_field_storage_delete($entity_type, $entity, $fields) { list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); - mongodb_collection('fields_current', $entity_type)->remove(array( - '_id' => (int) $entity_id, - )); + mongodb_collection('fields_current', $entity_type)->remove(array('_id' => (int) $entity_id), array('safe' => TRUE)); $entity_info = entity_get_info($entity_type); - mongodb_collection('fields_revision', $entity_type)->remove(array( - $entity_info['entity keys']['id'] => (int) $entity_id, - )); + mongodb_collection('fields_revision', $entity_type)->remove(array($entity_info['entity keys']['id'] => (int) $entity_id), array('safe' => TRUE)); } /** @@ -263,9 +259,7 @@ function mongodb_field_storage_field_storage_delete($entity_type, $entity, $fiel */ function mongodb_field_storage_field_storage_delete_revision($entity_type, $entity, $fields) { list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity); - mongodb_collection('fields_revision', $entity_type)->remove(array( - '_id' => (int) $revision_id, - )); + mongodb_collection('fields_revision', $entity_type)->remove(array('_id' => (int) $revision_id), array('safe' => TRUE)); } /** @@ -472,8 +466,8 @@ function _mongodb_field_storage_query_value($value, $operator, $type) { * Implements hook_field_attach_rename_bundle(). */ function mongodb_field_storage_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { - mongodb_collection('fields_current', $entity_type)->update(array('_bundle' => $bundle_old), array('_bundle' => $bundle_new), array('multiple' => TRUE)); - mongodb_collection('fields_revision', $entity_type)->update(array('_bundle' => $bundle_old), array('_bundle' => $bundle_new), array('multiple' => TRUE)); + mongodb_collection('fields_current', $entity_type)->update(array('_bundle' => $bundle_old), array('_bundle' => $bundle_new), array('multiple' => TRUE), array('safe' => TRUE)); + mongodb_collection('fields_revision', $entity_type)->update(array('_bundle' => $bundle_old), array('_bundle' => $bundle_new), array('multiple' => TRUE), array('safe' => TRUE)); } /** @@ -495,9 +489,9 @@ function mongodb_field_storage_entity_update($entity, $entity_type) { */ function mongodb_field_storage_field_attach_delete($entity_type, $entity) { list($entity_id, $revision_id) = entity_extract_ids($entity_type, $entity); - mongodb_collection('fields_current', $entity_type)->remove(array('_id' => (int) $entity_id)); + mongodb_collection('fields_current', $entity_type)->remove(array('_id' => (int) $entity_id), array('safe' => TRUE)); if ($revision_id) { - mongodb_collection('fields_revision', $entity_type)->remove(array('_id' => (int) $revision_id)); + mongodb_collection('fields_revision', $entity_type)->remove(array('_id' => (int) $revision_id), array('safe' => TRUE)); } } diff --git a/mongodb_queue/mongodb_queue.inc b/mongodb_queue/mongodb_queue.inc index 0d8f8c3..a3d728e 100644 --- a/mongodb_queue/mongodb_queue.inc +++ b/mongodb_queue/mongodb_queue.inc @@ -35,7 +35,7 @@ class MongoDBQueue implements DrupalQueueInterface { 'expire' => 0, ); return mongodb_collection($this->collection) - ->insert($item); + ->insert($item, array('safe' => TRUE)); } /** @@ -85,7 +85,7 @@ class MongoDBQueue implements DrupalQueueInterface { */ public function releaseItem($item) { return mongodb_collection($this->collection) - ->update(array('_id' => $item->_id), array('$set' => array('expire' => 0))); + ->update(array('_id' => $item->_id), array('$set' => array('expire' => 0)), array('safe' => TRUE)); } /** @@ -96,7 +96,7 @@ class MongoDBQueue implements DrupalQueueInterface { */ public function deleteItem($item) { mongodb_collection($this->collection) - ->remove(array('_id' => $item->_id)); + ->remove(array('_id' => $item->_id), array('safe' => TRUE)); } /** diff --git a/mongodb_queue/mongodb_queue.module b/mongodb_queue/mongodb_queue.module index 5bd9f53..520bbf2 100644 --- a/mongodb_queue/mongodb_queue.module +++ b/mongodb_queue/mongodb_queue.module @@ -32,7 +32,7 @@ function mongodb_queue_cron() { if (strpos($collection_name, '$') === FALSE && substr($collection_name, 0, $n) == "$db.") { // Split off the name of the database. $db->selectCollection(substr($collection_data['name'], $n)) - ->update($update_query, array('$set' => array('expire' => 0)), array('multiple' => TRUE)); + ->update($update_query, array('$set' => array('expire' => 0)), array('multiple' => TRUE, 'safe' => TRUE)); } } } diff --git a/mongodb_session/mongodb_session.inc b/mongodb_session/mongodb_session.inc index 8ee72ac..b22f481 100644 --- a/mongodb_session/mongodb_session.inc +++ b/mongodb_session/mongodb_session.inc @@ -179,7 +179,7 @@ function _drupal_session_write($sid, $value) { $collection = mongodb_collection(variable_get('mongodb_session', 'session')); $collection - ->update($key, $key + $fields, array('upsert' => TRUE)); + ->update($key, $key + $fields, array('upsert' => TRUE, 'w' => 0)); // Last access time is updated no more frequently than once every 180 seconds. // This reduces contention in the users table. @@ -193,7 +193,7 @@ function _drupal_session_write($sid, $value) { ->condition('uid', $user->uid) ->execute(); // Update MongoDB because we prefer that. - mongodb_collection('fields_current', 'user')->update(array('_id' => (int) $user->uid), array('$set' => $fields)); + mongodb_collection('fields_current', 'user')->update(array('_id' => (int) $user->uid), array('$set' => $fields), array('w' => 0)); // Throw a bone to entitycache, too. if (variable_get('entitycache_enabled', FALSE)) { cache_clear_all($user->uid, 'cache_entity_user'); @@ -349,7 +349,7 @@ function drupal_session_regenerate() { if (isset($old_session_id)) { $field = $is_https ? 'ssid' : 'sid'; mongodb_collection(variable_get('mongodb_session', 'session')) - ->update(array('sid' => $old_session_id), array('$set' => array($field => session_id()))); + ->update(array('sid' => $old_session_id), array('$set' => array($field => session_id())), array('w' => 0)); } } @@ -366,7 +366,7 @@ function _drupal_session_destroy($sid) { $field = $is_https ? 'ssid' : 'sid'; mongodb_collection(variable_get('mongodb_session', 'session')) - ->remove(array($field => $sid)); + ->remove(array($field => $sid), array('w' => 0)); // Reset $_SESSION and $user to prevent a new session from being started // in drupal_session_commit(). @@ -411,7 +411,7 @@ function _drupal_session_delete_cookie($name, $secure = NULL) { */ function drupal_session_destroy_uid($uid) { mongodb_collection(variable_get('mongodb_session', 'session')) - ->remove(array('uid' => $uid)); + ->remove(array('uid' => $uid), array('w' => 0)); } /** @@ -430,7 +430,7 @@ function _drupal_session_garbage_collection($lifetime) { // to '1814400'. At that value, only after a user doesn't log in after // three weeks (1814400 seconds) will his/her session be removed. mongodb_collection(variable_get('mongodb_session', 'session')) - ->remove(array('timestamp' => array('$lt' => REQUEST_TIME - $lifetime))); + ->remove(array('timestamp' => array('$lt' => REQUEST_TIME - $lifetime)), array('w' => 0)); return TRUE; } diff --git a/mongodb_watchdog/mongodb_watchdog.module b/mongodb_watchdog/mongodb_watchdog.module index 1e61bda..b7045cf 100644 --- a/mongodb_watchdog/mongodb_watchdog.module +++ b/mongodb_watchdog/mongodb_watchdog.module @@ -111,7 +111,7 @@ function mongodb_watchdog_watchdog(array $log_entry) { ); $collection = mongodb_collection(variable_get('mongodb_watchdog', 'watchdog')); $id = md5($log_entry['function'] . ':' . $log_entry['line'] . ':' . $log_entry['severity'] . ':' . $log_entry['type'] . ':' . $log_entry['message']); - $collection->update(array('_id' => $id), $newobj, array('upsert' => TRUE)); + $collection->update(array('_id' => $id), $newobj, array('upsert' => TRUE, 'safe' => TRUE)); $result = $collection->db->command(array('getlasterror' => 1)); $collection = $collection->db->selectCollection('watchdog_event_' . $id); if (empty($result['updatedExisting'])) { @@ -119,7 +119,7 @@ function mongodb_watchdog_watchdog(array $log_entry) { $command = array('create' => $collection->getName(), 'capped' => TRUE, 'size' => $max * 1000, "max" => $max); $collection->db->command($command); } - $collection->insert($event); + $collection->insert($event, 'safe' => TRUE); } /**