How can I confirm PHP MongoDB connection is properly recognizing replica sets? - php

Using the example from the manual:
$mongo = new Mongo("mongodb://sf2.example.com,ny1.example.com", array("replicaSet" => "myReplSet"));
When I check $mongo, it says it is indeed connected. I thought I could then call $mongo->isMaster() to get replica set details, but that doesn't work. Is that not a proper way of doing this?

isMaster isn't a PHP function (see http://www.php.net/manual/en/class.mongo.php for a list of functions available in the Mongo class). You can do:
$result = $mongo->myDb->command(array("isMaster" => 1));
This runs the isMaster command on the myDb database (it doesn't matter what db you run it on).

Related

How to explain mongodb query plan with PHP MongoDB driver

The current PHP MongoDB client doesn't include an explain() feature in the query builder. How the explain information could be retrieved for the query below?
use MongoDB\Client;
$client = new Client($mongoUri);
$database = $client->selectDatabase('someDB');
$collection = $database->selectCollection('collectionName');
$results = $collection->find(['key' => 'value'], ['sort' => ['key2' => -1]]);
MongoDB client: https://www.php.net/manual/en/class.mongodb.php
MongoDB::command could be a way to the solution. https://www.php.net/manual/en/mongodb.command.php
Note! There is a deprecated mongodb client that has this feature.
One way of doing it:
Get the find command that the driver sends to the database for your query. It would look like this. Generally these can be obtained from command monitoring, hopefully this works.
Send an explain command giving the find command as the argument using the driver-provided facility to send arbitrary commands to the database. Or, use mongo shell for this part.

Call stored procedure/function with PHP in Mongo DB sharded cluster

I am using MongoDB 2.6 with two shard clusters config.
I want to call a function dataStats() that I create and store in MongoDB. This is my PHP script:
$client = new Mongo();
$db = $client->mydata;
$db->system->js->save(array("_id"=>"dataStats",
"value"=>new MongoCode("function() { ... }")));
$db->execute("dataStats()");
This code gives me this error:
'err' => 'Error: can\'t use sharded collection from db.eval',
'code' => 16722
The reason is $db->execute method is using Mongo db.eval command which is not supported with sharded collections. Is there a workaround for this issue? How can we call a stored procedure in sharded MongoDB from PHP?
There's no workaround. db.eval doesn't work with sharded collections. You should avoid using it if at all possible, anyway.

Renaming a Mongo Collection in PHP

PHP's Mongo driver lacks a renameCommand function. There is reference to do this through the admin database. But it seems more recent versions of the Mongo driver don't let you just "use" the admin database if do don't have login privileges on that database. So this method no longer works. I've also read this doesn't work in sharded environments although this isn't a concern for me currently.
The other suggestion people seem to have is to iterate through the "from" collection and insert into the "to" collection. With the proper WriteConcern (fire and forget) this could be fairly fast. But it still means pulling down each record over the network into the PHP process and then uploading it back over the network back into the database.
I ideally want a way to do it all server-side. Sort of like an INSERT INTO ... SELECT ... in SQL. This way it is fast, network efficient and a low load on PHP.
I have just tested this, it works as designed ( http://docs.mongodb.org/manual/reference/command/renameCollection/ ):
$mongo->admin->command(array('renameCollection'=>'ns.user','to'=>'ns.e'));
That is how you rename an unsharded collection. One problem with MR is that it will change the shape of the output from the original collection. As such it is not very good at copying a collection. You would be better off copying it manually if your collection is sharded.
As an added note I upgraded to 1.4.2 (which for some reason comes out from the pecl channel into phpinfo() as 1.4.3dev :S) and it still works.
Updates:
Removed my old map/reduce method since I found out (and Sammaye pointed out) that this changes the structure
Made my exec version secondary since I found out how to do it with renameCollection.
I believe I have found a solution. It appears some versions of the PHP driver will auth against the admin database even though it doesn't need to. But there is a workaround where the authSource connection param is used to change this behavior so it doesn't auth against the admin database but instead the database of your choice. So now my renameCollection function is just a wrapper around the renameCollection command again.
The key is to add authSource when connecting. In the below code $_ENV['MONGO_URI'] holds my connection string and default_database_name() returns the name of the database I want to auth against.
$class = 'MongoClient';
if( !class_exists($class) ) $class = 'Mongo';
$db_server = new $class($_ENV['MONGO_URI'].'?authSource='.default_database_name());
Here is my older version that used eval which should also work although some environments don't allow you to eval (MongoLab gives you a crippled setup unless you have a dedicated system). But if you are running in a sharded environment this seems like a reasonable solution.
function renameCollection($old_name, $new_name) {
db()->$new_name->drop();
$copy = "function() {db.$old_name.find().forEach(function(d) {db.$new_name.insert(d)})}";
db()->execute($copy);
db()->$old_name->drop();
}
you can use this. "dropTarget" flag is true then delete exist database.
$mongo = new MongoClient('_MONGODB_HOST_URL_');
$query = array("renameCollection" => "Database.OldName", "to" => "Database.NewName", "dropTarget" => "true");
$mongo->admin->command($query);

PHP's SQLSRV driver would not complete a Stored Procedure query that works normally elsewhere

So I'm confident that the stored procedure works, I've tested it in SQL Server Management Studio just fine and it runs in other service instances. The query used to run this SP follows;
exec sp_getAgentCommissionDetails_v3 201000023762230, 5
So it runs fine on SSMS and under the old MSSQL driver. But I run the query with SQLSRV like so;
function mssql_query($string, $linkID = null, $batch = 0) {
if (!$linkID) {
global $dbhandle;
$linkID = $dbhandle;
}
// SQLSRV_CURSOR_KEYSET ensures mssql_num_rows() works in most cases. Default scrollablility does not support this.
return sqlsrv_query($linkID, $string, array(), array("Scrollable" => SQLSRV_CURSOR_KEYSET));
}
The function is named mssql_query because we're updating an old system from MSSQL to SQLSRV, but we're working with an extremely messy old system. So rather than trying to refactor it we're overwriting the MSSQL_query function (having disabled the mssql extension) with one that uses SQLSRV.
$dbhandle is our SQLSRV connection resource. Other queries run just fine using this method.
So my question- is there any reason the query to run a stored procedure would not run under this SQLSRV function?
Couple notes on my troubleshooting;
I'm aware SQLSRV and PDO have a specific method to run stored procedures. Using that is not an option due to the massive codebase that uses the method above in various places and because we haven't got the man hours to refactor every page.
I pulled up SQLSRV_errors() and it returned 'Executing SQL directly; no cursor'. After a bit of research this seems to be a bug in the driver that returns this generic error message instead of a more specific useful one, so it can mean very many things.
There are no cursors or loops involved in the stored procedure.
Found a solution to this. SQLSRV seems to by default treat warnings as errors. Fixed this with a config change in the connection file;
sqlsrv_configure("WarningsReturnAsErrors",0);
It now runs fine.

MongoDb PHP Driver - Aggregate query with readPreference?

Using php driver 1.3.2 and mongodb 2.2, I am trying to use readPreference to direct an aggregate query to one of the secondaries in my replica set. Seems whatever I try, the aggregate query is executed on the primary server.
Basic example:
$db = new \MongoClient('rs1.example:27017,rs2.example:27017,rs3.example:27017', array('replicaSet' => 'myRs') );
$db->setReadPreference( \MongoClient::RP_SECONDARY );
$results = $db->tracking->sessions->aggregate( array( ... ) );
I enabled MongoLog and got the following results:
The aggregate method shows: REPLSET INFO: - connection: type: PRIMARY
If I use find instead, it shows: REPLSET INFO: - connection: type: SECONDARY
Is this a bug with the php driver? Anyone else run into this? Thought I would toss it on SO before adding it as a bug in their Jira.
All command queries through the PHP driver are currently directed to primary servers. We have several old tickets related to this, originating with requests to direct count commands to secondaries, but it was a non-trivial change that requiring checking the command against a whitelist to ensure it is read-only. The current ticket tracking this fix is PHP-535, which I linked to the issue you opened, PHP-662.
If you need an immediate work-around, you can call find() on the $cmd collection directly, passing the same $command array argument that you'd typically pass to MongoDB::command() as the argument to find(). Kristina documented this solution in this JIRA comment, and although she used slaveOkay() in that example, it should also work with read preferences.

Categories