I am using Pool object in PHP pthread, and made the following test script, to see how the pooling should work. I tought, that what pooling should do, is to get a given number of tasks, open up a maximum x number of workers, and assign them the tasks, and as soon as a worker finishes with a task, if more tasks are available, assign to that worker a new task.
Given the below example, and the above assumption:
class Work extends Threaded {
public $id;
public function __construct($id) {
$this->id = $id;
}
public function run() {
if ($this->id == 0) {
sleep(3);
echo $this->id . " is ready\n";
return;
} else {
echo $this->id . " is ready\n";
return;
}
}
}
$pool = new Pool(2, 'Worker', []);
for ($i=0; $i<4; $i++) $pool->submit(new Work($i));
while ($pool->collect());
$pool->shutdown();
I was expecting this script to output the following information:
1 is ready
2 is ready
3 is ready
0 is ready
because, there are essentially 2 workers available, and because of the sleep operatin the first worker stumbles upon, task 1,2,3 must be completed by the second worker.
Instead of this, the output I am getting is:
1 is ready
3 is ready
0 is ready
2 is ready
It is clear, that worker 1, gets assigned job 0, and job 2 at the get go, thus worker 2, after finishing job 1 and 3, just waits, instead of taking over job 2 from worker 1.
Is this a bug? Or is this intended to work this way?
My PHP version:
PHP 7.2.14 (cli) (built: Jan 9 2019 22:23:26) ( ZTS MSVC15 (Visual C++ 2017) x64 )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
For some reason my Docker has crapped itself now that I've updated Windows to 1809, so posting untested. (So sorry, no output to give atm)
Modified existing code I use in a project with your counter + sleep.
$pool = new Pool(2);
foreach ([0,1,2,3] as $count) {
$pool->submit(
new class ($count) extends Threaded
{
private $count;
public function __construct(int $count)
{
$this->count= $count;
}
public function run()
{
if ($this->count== 0) {
sleep(3);
echo $this->count . " is ready\n";
} else {
echo $this->count . " is ready\n";
}
}
}
);
}
while ($pool->collect());
$pool->shutdown();
I use anonymous class (new class ($count) extends Threaded) as the submit() param.
On the server this runs perfectly, using a Docker instance running PHP ZTS 7.2.13 on Alpine 3.8
Let me answer: from what I know about pthreads in php, pool is like number of proccessing php.exe that can be run at the same times.
So in your case, you define two pool by using new Pool(2, 'Worker', []);
So let's make abstract explanation about it. There is 2 Pool, call it as PoolA and PoolB.
Loop from 0 to 3, each loop submit task to Pool.
There are 4 tasks from 0 to 3, lets call them by task0, task1, task2, task3.
When loop occur, from my perspective, it should be queue like this
PoolA -> submit task0
PoolB -> submit task1
PoolA -> submit task2
PoolB -> submit task3
But from class Work that will be task0, ... till task3.
Situation/Condition
You define some logic in run() => when parameter(in this case $id from constructor) is 0, then sleep(3).
From this situation, PoolA is which submit task0 that contains parameter($id) is value 0, PoolA will wait for 3 seconds. PoolA also submit task2.
On the other hand, PoolB submit task1 and task3, from this situation, doesn't need to wait for 3 seconds.
So when while($pool->collect()); is run, possible queue that's most likely happen
task1 (PoolB)
task3 (PoolB)
task0 (PoolA) ->>>> PoolA delayed because from task0 needs to sleep for 3 seconds
task2 (PoolA)
So I think it's correct when outputs are
1 is ready
3 is ready
0 is ready
2 is ready
There is a questions.
Why is only PoolA that delayed, even if PoolA delayed why task2 didn't submit to PoolB or why task1 or task3 not submit to PoolA??
Well, I don't understand too. I have task similar to yours, after many experiments, I'm not sure pthreads which use Pool & Threaded is multi-threading or multiprocessing.
Echoing from the individual threads can be deceiving.
I often find that they seem like they are executing before they are even called. I'd recommend avoiding echoing from inside threads, unless you don't care about the order, as it can be still be useful to test for specific circumstances, etc.
Below is some code which should resolve any questions of when the code is executing, as this code sorts the results by the actual time they executed. (It's also a nice example of how to get results back from a thread pool.)
<?php
class Work extends Threaded {
public $id;
public $data;
private $complete = false;
public function __construct($id) {
$this->id = $id;
}
public function run() {
$temp = array();
if ($this->id == 0) {
echo "<pre>".$this->id . " started (from inside threaded)";
$temp[] = array(microtime(true), $this->id . " started");
sleep(3);
}
echo "<pre>".$this->id . " is ready (from inside threaded)";
$temp[] = array(microtime(true), $this->id . " is ready");
$this->data = (array) $temp; // note: it's important to cast as array, otherwise you will get a volitile
$this->complete = true;
}
public function isDone() {
return $this->complete;
}
}
// we create a custom pool, to pass on our results
class ExamplePool extends Pool {
public $dataAr = array(); // used to return data after we're done
private $numTasks = 0; // counter used to know when we're done
private $numCompleted = 0; // keep track of how many threads finished
/**
* override the submit function from the parent
* to keep track of our jobs
*/
public function submit(Threaded $task) {
$this->numTasks++;
parent::submit($task);
}
/**
* used to wait until all workers are done
*/
public function process() {
// Run this loop as long as we have
// jobs in the pool
while ($this->numCompleted < $this->numTasks) {
$this->collect(function (Work $task) {
// If a task was marked as done, collect its results
if ($task->isDone()) {
//this is how you get your completed data back out [accessed by $pool->process()]
$this->dataAr = array_merge($this->dataAr, $task->data);
$this->numCompleted++;
}
return $task->isDone();
});
}
// All jobs are done
// we can shutdown the pool
$this->shutdown();
return $this->dataAr;
}
}
$pool = new ExamplePool(4);
for($i=0; $i<4; $i++) {
$pool->submit(new Work($i));
}
$retArr = $pool->process();
usort($retArr, 'sortResultsByTime'); // sort the results by time
// echo out the sorted results
echo "<br><br>";
for($i=0;$i<count($retArr);$i++){
echo number_format($retArr[$i][0], 4, ".", "").' '.$retArr[$i][1]."\n";
}
function sortResultsByTime($a, $b) {
return $a[0] > $b[0];
}
?>
Please note the code above yields this for me:
0 started (from inside threaded)
0 is ready (from inside threaded)
1 is ready (from inside threaded)
2 is ready (from inside threaded)
3 is ready (from inside threaded)
1609458117.8764 0 started
1609458117.8776 1 is ready
1609458117.8789 2 is ready
1609458117.8802 3 is ready
1609458120.8765 0 is ready
And as expected, the stuff echoed from inside the threads seems weird, however if you store the results, and sort them by the time they were executed, you can see it acts as expected.
Related
I do not understand what is going on with my migration script. So a have a collection with 40+m records in it, and historically that collection did not have a strict model, so I'm working on adding default values for some optional fields, for example, if the document does not have deleted_at I'll add it with the null value.
Basically, I'm taking documents in batches by 300, checking if a document should be updated and if so updating it. All was fine, I was able to update 12M documents in 9 hours. But after that, something weird started to happen, first of all, it started to work much much slower, like 100k documents in an hour which is ~10x slower than was before. Also from the logs, I can see that script updating documents pretty fast (I have a bunch of log entries related to updated documents every second), but if I run the count query to get the number of modified documents, the amount is not increasing so often. For example, depending on logs in 10 seconds 400 rows were updated, but the number of modified documents did not increase when the count query runs. The number of the modified documents simply increases once per some period of time, for example, the number can be the same for 2-3 minutes, and then at some point, it increases on 4k rows.
So I do not understand why at some point mongo starts running updates with some delay, scheduling them or something, and why it starts to work slower?
The script is pretty big, but I'll try to share the simplified version, so you can see how I'm looping through documents:
class Migration {
private Connection $connection;
public function __construct(Connection $collection)
{
$this->connection = $collection;
}
public function migrate(): void
{
$totalAmount = $this->connection->collection('collection')->count();
$chunkSize = 300;
$lastIdInBatch = null;
for ($i = 0; $i < $totalAmount; $i += $chunkSize) {
$aggregation = [];
$aggregation[] = [
'$sort' => ['_id' => 1],
];
if ($lastIdInBatch !== null) {
$aggregation[] = [
'$match' => [
'_id' => [
'$gt' => new ObjectId($lastIdInBatch),
],
],
];
}
$aggregation[] = [
'$limit' => $chunkSize,
];
$documents = $this->connection->collection('collection')->raw()->aggregate(
$aggregation
);
$lastIdInBatch = $documents[array_key_last($documents)]['_id'];
foreach ($documents as $document) {
// checks to see if we need to update the document
// ....
if (!empty($changes)) {
$updated = $this->connection
->collection('collection')
->where('_id', document['_id'])
->update($changes);
if ($updated) {
Log::info('row udpated', ['product_id' => document['_id']]) // I see multiple of this logs each seconds, but no changes in database
}
}
}
}
}
}
Issue self-healed after restart of kubernetes pod, so it seems like wasn't the issue with mongo
I have a Chat.php file containing everything query-related. Then there is a Core.php containing connection to database and basic functions used in Chat.php like "query" and "rows" which processes the "query" into array.
In Chat.php there are two functions, the second one printing content of the first when using print_r($this->rows());. checkForLastMessage() is supposed to check one table to see if there are new messages to be pulled from another table with function getNewMessages().
This is how it looks like:
Core.php
<?php
class Core
{
...
public function query($sql)
{
$this->result = $this->db->query($sql);
}
public function rows()
{
for($x = 1; $x <= $this->db->affected_rows; $x++)
{
$this->rows[] = $this->result->fetch_assoc();
}
return $this->rows;
}
}
Chat.php
<?php
class Chat extends Core
{
public function checkForLatestMessage($chatid, $iam)
{
$userinchat='for'.$iam;
$this->query("SELECT anonchat.$userinchat FROM anonchat WHERE anonchat.chatid=$chatid");
$printarray = Array();
$printaray = '';
foreach( $this->rows() as $id )
{
$printarray[] = $id[$userinchat];
}
if($printarray[0] != '')
{
$this->getNewMessages($chatid, $printarray[0]);
}
}
public function getNewMessages($chatid, $requiredMessages)
{
$this->query("SELECT anonmessage.content, anonmessage.timeposted FROM anonmessage WHERE anonmessage.messageid IN ($requiredMessages) ORDER BY anonmessage.timeposted ASC");
print_r($this->rows());
}
The last print_r contains elements from the previous function. I don't know why that is.
Edit. This is the output:
Array ( [0] => Array ( [for1] => 2,4,6 ) [1] => Array ( [content] =>
Message 2 [timeposted] => 2017-08-04 16:12:34 ) [2] => Array (
[content] => Message 4 [timeposted] => 2017-08-04 16:12:48 ) [3] =>
Array ( [content] => Message 6 [timeposted] => 2017-08-04 16:13:03 ) )
Element [0] of array (the one with "for1") is remaining from previous function.
To answer your question, you're initializing the class property $this->rows in the first method and then the 2nd method is appending to it. You need to reset $this->rows before adding to it.
public function getNewMessages($chatid, $requiredMessages)
{
$this->rows = null;
$this->query("SELECT anonmessage...");
print_r($this->rows());
}
Or better yet, reset the variable in the query() method. That way you don't have to do it each time.
Please don't take offense when I say that this is a bad design.
You're trying to write your own DAL (Data Abstraction Layer). This is a very complicated task and there are already lots of implementations out there. The Core class is going to become massive, complicated, and unwieldy when you try to adapt it to a dozen other classes.
PHP only supports a single inheritance so right off the bat you shot your self in the foot because any class that needs DB interactions will have to extend Core and you won't be able to extend anything else.
Consider keeping things simple for now and let each method handle their own queries and DB interactions. Focus on major concepts like DRY, encapsulation and keeping your classes focused on their responsibility.
Ex. checkForLatestMessage() what is this supposed to do? It sounds like should check for messages and then return a boolean (true|false) but instead it is calling getNewMessages() which outputs some data.
I don't know enough about your application to really suggest something useful but this feels a bit better than the path you're heading down. Notice we're not inheriting from Core so you're free to inherit from something else. The methods are concise and do what they say. You'd probably also save a few lines of code this way and it's easier to read.
<?php
class Chat
{
public function hasNewMessages($chatid, $iam)
{
// Query using PDO properly
return (bool)$hasNewMessages;
}
public function getNewMessages($chatid, $requiredMessages)
{
// Query using PDO properly
// An array of data or objects
return $messages;
}
}
/******** Client Code ***********/
$chat = new Chat();
if ($chat->hasNewMessages()) {
foreach ($chat->getNewMessages($id, $required) as $message) {
// $message
}
}
Just some of my thoughts... good luck.
The question is in PHP, but applies to any language using the xUnit framework.
I want a mock, that expects 140 calls to method jump.
I need to verify, that at least once there is a call with 500 as parameter.
I don't care if all the calls are 500, but I need at least one that is called with 500.
$mock = $this->getMock('Trampoline', ['jump']);
$mock->expects($this->atLeastOnce())
->method('jump')
->with($this->equalTo(500))
->will($this->returnValue(true));
$sportsman->setTramploine($mock);
$sportsman->jumpToRandomHeights($times = 140); // this calls Trampoline->jump
// I need to verify the sportsman had jumped
// to the height of 500 at least once out of the 140 jumps he is performing
In the current code, the test fails after the first invocation of jump because the first invocation had a value different of 500, meaning the atLestOnce here only indicates that the method should be called, but not that it should be called with specific value among other calls.
Solution
The missing piece of information was using callbacks inside the with. Thanks to edorian's answer below this is what worked out:
$testPassed = false;
$checkMinHeight = function ($arg) use(&$testPassed)
{
if($arg === 500)
$testPassed = true;
// return true for the mock object to consider the input valid
return true;
}
$mock = $this->getMock('Trampoline', ['jump'])
->expects($this->atLeastOnce())
->method('jump')
->with($checkMinHeight)
->will($this->returnValue(true));
$sportsman->setTramploine($mock);
$sportsman->jumpToRandomHeights($times = 1000); // this calls Trampoline->jump
// I need to verify the sportsman had jumped
// to the height of 500 at least once out of the 1000 jumps he is performing
$this->assertTrue($testPassed, "Sportsman was expected to
jump 500m at least once");
You can but the best implementation withing PHPUnits mocking API, that I could come up with, still looks quite creepy.
Another way to solve this is a little more readable way would be to create your own subclass of Trampoline and implement it there.
But for the challenge:
Assuming this class:
<?php
class FancyMocking {
function doThing($value) { }
}
and that we have $x calls and one of those has to have a $value > 200:
<?php
class FancyMockingTest extends PHPUnit_Framework_TestCase {
public function testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500() {
$maxInvocations = 200;
$mock = $this->getMock('FancyMocking');
$mock->expects($this->exactly($maxInvocations))
->method('doThing')
->with($this->callback(function ($value) use ($maxInvocations) {
static $invocationCount = 0;
static $maxValue = 0;
$maxValue = max($value, $maxValue);
/* The assertion function will be called twice by PHPUnit due to implementation details, so the *2 is a hack for now */
if (++$invocationCount == $maxInvocations * 2) {
$this->assertGreaterThan(200, $maxValue, 'in 500 tries the max value didn\'t to over 200');
}
return true;
}))
->will($this->returnCallback(function ($value) {
return $value >= 200;
}));
for($i = $maxInvocations - 2; $i; --$i) {
$mock->doThing(50);
}
var_dump($mock->doThing(250));
var_dump($mock->doThing(50));
}
}
This will produce:
PHPUnit 3.7.9 by Sebastian Bergmann.
.bool(true)
bool(false)
Time: 0 seconds, Memory: 2.75Mb
OK (1 test, 2 assertions)
Meaning the call with 250 returns true an the whole test case works.
If it fails:
To make it fail we change var_dump($mock->doThing(250)); to var_dump($mock->doThing(70)); and run it again:
PHPUnit 3.7.9 by Sebastian Bergmann.
Fbool(false)
Time: 0 seconds, Memory: 2.75Mb
There was 1 failure:
1) FancyMockingTest::testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500
Expectation failed for method name is equal to <string:doThing> when invoked 200 time(s)
in 500 tries the max value didn't to over 200
Failed asserting that 70 is greater than 200.
.../FancyMockingTest.php:29
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
The solution that doesn't use PHPUnits mocking API would be something like
class FancyMockingFakeImplementation extends FancyMocking, using that instead of the mock and writing the custom logic there.
It is not so bad rendering RSS feeds from an index.php page I have seen a lot of examples of it. But I am having problems doing it from a class. This is what I have and I am sure I have many errors... PHP is not my strong point so if you could let me know where i am going wrong i would apprecite it...
<?php
class RssDisplay {
protected $fromrss;
protected $allitemscollected_feed;
public function __construct($urllink, $total_items){
$this->fromrss = $urllink;
$this->allitemscollected_feed = $total_items;
}
public function getItems($allitemscollected_feed){
$feed = simplexml_load_file($this->fromrss, $this->allitemscollected_feed);
return $feed;
$collected_items[] =$feed;
$this->set('collected_items', $collected_items);
}
foreach('allitemscollected' as $feed){
$items->title;
$items->pubDate;
$items->description;
}
}
from my index this is what I am doing:
$feed = new RssDisplay('http://feeds.feedburner.com/insidethehall?format=xml');
For starters i would advice you to take a look at Foreach and PHP OPP Basics because
Your foreach is not in any method
foreach('allitemscollected' as $feed){ is not valid because foreach first argument needs to be array_expression
You class is wrong
You did not print or echo anything
return $feed; was called early which would make other codes invisible
$this->set( method those not exists yet it was called $this->set('collected_items', $collected_items);
simplexml_load_file second argument needs to be a valid class name that extends SimpleXMLElement yet an integer is passed
Your Simple Class you look like this
class RssDisplay {
protected $maxItems;
private $xml;
public function __construct($url, $maxItems = 0) {
$this->xml = simplexml_load_file($url, "SimpleXMLIterator");
$this->maxItems = $maxItems;
}
public function getItems() {
return $this->xml->channel->item;
}
public function simpleDisplay() {
$it = ($this->maxItems == 0) ? $this->xml->channel->item : new LimitIterator($this->xml->channel->item, 0, $this->maxItems);
foreach ( $this->xml->channel->item as $feed ) {
printf("<div><h2><a href='%s'>%s</a></h2><i>%s</i><p>%s</p></div><br />", $feed->link, $feed->title, $feed->pubDate, $feed->description);
}
}
}
$rss = new RssDisplay("http://feeds.feedburner.com/insidethehall?format=xml");
$rss->simpleDisplay();
Output
Grantland previews the HoosiersFri, 02 Nov 2012 19:22:21 +0000Grantland: College Basketball Team Previews: Indiana There’s no shortage of offensive firepower with the Hoosiers, and the great thing about Tom Crean is that he’s never afraid to use it. Indiana was well inside the top half of the country in tempo last season, and with an extra year of chemistry it’s a good bet [...]Notebook: Creek shines in return from injuryFri, 02 Nov 2012 02:57:32 +0000Maurice Creek got off the bench and walked toward the Assembly Hall scorer’s table with just over 11 minutes left in the first half of Thursday night’s exhibition game against Indiana Wesleyan. Creek checked in with the official scorer, then waited patiently for the moment he’s been thinking about for more than 20 months — [...]
....... So may more
Your construct takes two paremeter, but you
used it with single paremeter.
i used to have a function defined like this (that is working fine under ubuntu 9.10):
public function __toString( $surNameFirst = false) {
if ($this->givenName . $this->surname == '') return null;
else .......
}
after i have updated my machine to ubuntu 10.04( and php version Version: 5.3.2-1ubuntu4.2
) my app starts to show an error like this one ==>
Fatal error: Method Application_Model_Person::__tostring() cannot take arguments in /home/speshu/Development/where/application/models/Person.php on line 39
Call Stack:
0.0001 616576 1. {main}() /home/speshu/Development/where/public/index.php:0
0.0294 1008248 2. Zend_Application->bootstrap() /home/speshu/Development/where/public/index.php:35
0.0294 1008328 3. Zend_Application_Bootstrap_BootstrapAbstract->bootstrap() /usr/local/lib/ZendFramework-1.10.0/library/Zend/Application.php:355
0.0294 1008328 4. Zend_Application_Bootstrap_BootstrapAbstract->_bootstrap() /usr/local/lib/ZendFramework-1.10.0/library/Zend/Application/Bootstrap/BootstrapAbstract.php:582
0.0387 1991416 5. Zend_Application_Bootstrap_BootstrapAbstract->_executeResource() /usr/local/lib/ZendFramework-1.10.0/library/Zend/Application/Bootstrap/BootstrapAbstract.php:618
0.0387 1991776 6. Bootstrap->_initDoctrineCLI() /usr/local/lib/ZendFramework-1.10.0/library/Zend/Application/Bootstrap/BootstrapAbstract.php:665
0.0387 1991856 7. Bootstrap->_initDoctrine() /home/speshu/Development/where/application/Bootstrap.php:66
0.0406 2245200 8. Doctrine_Core::loadModels() /home/speshu/Development/where/application/Bootstrap.php:93
As of version 5.3 The __toString magic method can no longer accept arguments.
In any case should you really be doing that with a magic method? Why not declare toString($whatever) instead?
function __toString() {
if(func_num_args()>0) {
$surNameFirst=func_get_arg(0);
} else {
$surNameFirst=false;
}
....
This is how I got around the problem, it's dirty and ugly... but it worked to get the system working again before finding a more permanent solution.
You should be able to figure out how best to extend this to suite your needs, be aware that it's probably best to pass in an assosiative array if using this method, as it would be easier to access the data.
I think it's very ugly to invoke $obj->__toString();
it should be better to call just echo $obj ; if you don't want to pass arguments and call $obj->toString($param) ; if you want to specify arguments.
Another Possible work around.
class test
{
var $toStringArgs = array();
function SetToStringArgs()
{
$this->toStringArgs = func_get_args();
}
function __toString()
{
var_dump($this->toStringArgs);
}
}
$test = new test();
$test->SetToStringArgs('firstname','lastname');
$test->__toString();