Laravel क्यू जॉब में कैप्चा कैसे हल करें (PHP SDK)

laravel queue captcha - How to Solve CAPTCHAs in a Laravel Queue Job (PHP SDK)

Laravel क्यू जॉब में कैप्चा हल करने के लिए, जॉब के handle method के अंदर CapSkip PHP क्लाइंट को कॉल करें और टोकन उसी जॉब से सबमिट करें। कॉल छोटी है। सावधानी उन तीन संख्याओं में चाहिए जो Laravel कुछ ही सेकंड में पूरी होने वाली जॉब के हिसाब से डिफ़ॉल्ट रूप में देता है: वर्कर हर जॉब को 60 सेकंड देता है, क्यू 90 सेकंड के बाद जॉब को किसी दूसरे वर्कर को सौंप देती है, और हर जॉब को एक ही कोशिश मिलती है। reCAPTCHA हल 300 सेकंड तक पोल कर सकता है, इसलिए डिफ़ॉल्ट सेटिंग्स पर Laravel क्यू कैप्चा जॉब या तो बीच में ही खत्म कर दी जाती है या हल चलते रहने के दौरान ही failed मार्क कर दी जाती है, और रीट्राई जोड़ते ही वह दो बार चल सकती है। यह गाइड जॉब, timeouts, एक रीट्राई नियम, और वह एक चीज़ कवर करती है जो वर्कर के Windows पर चलने से बदल जाती है।

आपको क्या चाहिए

  • Laravel 11 या उसके बाद का संस्करण। जॉब वह अकेला Queueable trait इस्तेमाल करती है जो 11 में आया; यहाँ की बाकी हर चीज़ 10 पर भी काम करती है। Laravel 13 जॉब सेटिंग्स को #[Tries(3)] जैसे attributes के रूप में भी लिख सकता है, और नीचे इस्तेमाल की गई सादी properties वहाँ भी काम करती हैं।
  • CapSkip PHP पैकेज, जिसे curl और json extensions के साथ PHP 8.0 या उसके बाद का संस्करण चाहिए। जाँच लें कि जो php.ini आपका वर्कर असल में लोड करता है, उसमें curl चालू है, क्योंकि Windows पर यह कभी-कभी comment किया हुआ ही छूट जाता है।
  • एक क्यू कनेक्शन। नए Laravel ऐप database driver इस्तेमाल करते हैं, और नीचे के उदाहरण भी यही करते हैं।
  • किसी Windows मशीन पर चल रहा CapSkip। Local mode में वह केवल उसी मशीन के लिए 127.0.0.1 पर जवाब देता है, जो उसी PC पर चल रहे वर्कर के लिए ठीक है। Server mode में वह आपके नेटवर्क पते या पब्लिक IP पर सुनता है, ताकि Linux सर्वर, Forge या किसी भी दूसरे होस्ट पर चल रहा Laravel ऐप API के ज़रिए उस तक पहुँच सके। दोनों यहाँ सेट किए जाते हैं: कनेक्शन सेटिंग्स.
# Run in the Laravel project root
composer require capskip/capskip

चरण 1: क्लाइंट को container में रजिस्टर करें

सॉल्वर का पता जॉब में नहीं, configuration में रखें, ताकि वही कोड लोकल सॉल्वर के साथ भी चले और सर्वर वाले के साथ भी। config/services.php में एक block जोड़ें, और अपने service provider में एक singleton binding।

// config/services.php
'capskip' => [
    'key' => env('CAPSKIP_API_KEY', 'capskip'),
    'host' => env('CAPSKIP_HOST', '127.0.0.1'),
    'port' => (int) env('CAPSKIP_PORT', 8080),
],

// app/Providers/AppServiceProvider.php, inside register()
$this->app->singleton(\CapSkip\CapSkip::class, fn () => new \CapSkip\CapSkip([
    'apiKey' => config('services.capskip.key'),
    'host' => config('services.capskip.host'),
    'port' => config('services.capskip.port'),
]));

क्लाइंट एनवायरनमेंट वेरिएबल कभी खुद नहीं पढ़ता, इसीलिए binding हर वैल्यू को अंदर पास करती है। ऐप में कहीं और इन्हें env() से नहीं, config() से पढ़ें: config:cache चलाने के बाद, config फ़ाइलों के बाहर env() null लौटाता है।

चरण 2: एक ही जॉब में हल और सबमिट

handle() पर क्लाइंट का type-hint दें, और Laravel singleton inject कर देता है। जॉब पेज से sitekey पढ़ती है, हल करती है, और फॉर्म पोस्ट करती है।

<?php

namespace App\Jobs;

use CapSkip\CapSkip;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Http;

class SubmitSignup implements ShouldQueue
{
    use Queueable;

    public function __construct(public string $pageUrl, public array $fields) {}

    public function handle(CapSkip $solver): void
    {
        $html = Http::timeout(30)->get($this->pageUrl)->throw()->body();
        preg_match('/data-sitekey="([^"]+)"/', $html, $m);

        // Solve and submit in one job: the token expires in two minutes.
        $token = $solver->recaptcha($m[1], $this->pageUrl)['code'];

        Http::asForm()->timeout(30)
            ->post($this->pageUrl, $this->fields + ['g-recaptcha-response' => $token])
            ->throw();
    }
}

इसे पेज और फॉर्म के बाकी फ़ील्ड के साथ, इसके अपने कनेक्शन और क्यू पर dispatch करें, जिन्हें अगला चरण परिभाषित करता है:

SubmitSignup::dispatch('https://example.com/signup', ['email' => 'YOUR_EMAIL'])
    ->onConnection('captcha')
    ->onQueue('captcha');

हल और सबमिट को साथ रखें। reCAPTCHA टोकन लगभग दो मिनट तक वैध रहता है, और किसी दूसरी, chained जॉब को सौंपा गया टोकन यह पूरा समय दूसरे काम के पीछे इंतज़ार करते हुए बिता सकता है। reCAPTCHA टोकन की वैधता अवधि पर लिखी पोस्ट इस समय सीमा को विस्तार से समझाती है। PHP क्लाइंट synchronous है, इसलिए पूरे हल के दौरान वर्कर प्रोसेस व्यस्त रहता है; Laravel में कई हल साथ-साथ चलाने का तरीका ज़्यादा वर्कर प्रोसेस हैं, कोई दूसरा क्लाइंट नहीं।

चरण 3: timeouts को सही क्रम में लगाएँ

Laravel क्यू कैप्चा जॉब पर चार समय सीमाएँ चलती हैं। अंदर से बाहर की ओर:

  • CapSkip की अपनी सेटिंग्स। reCAPTCHA टास्क किसी खाली thread के लिए 250 सेकंड तक इंतज़ार कर सकता है (Wait Timeout) और हल करने के लिए उसे 250 सेकंड मिलते हैं (Row Timeout)। इनमें से किसी का भी समय खत्म होने पर CapSkip टास्क को फेल कर देता है और क्लाइंट एक ApiException उठाता है, बशर्ते नीचे बताई गई क्लाइंट की अपनी सीमा पहले ही fire न हो चुकी हो।
  • क्लाइंट की पोलिंग। recaptcha() ज़्यादा से ज़्यादा recaptchaTimeout तक पोल करता है, जो डिफ़ॉल्ट रूप से 300 सेकंड है, और फिर एक TimeoutException उठाता है। ऊपर के कोड में इसके पहले और बाद की दोनों HTTP कॉल 30 सेकंड प्रति कॉल पर सीमित हैं, इसलिए जब CapSkip सामान्य रूप से जवाब देता है, तो पूरा handle() लगभग छह मिनट के भीतर पूरा हो जाता है। लेकिन 300 सेकंड की समय सीमा पोल के बीच में जाँची जाती है, और क्लाइंट की भेजी हर रिक्वेस्ट 120 सेकंड तक अटकी रह सकती है, इसलिए जवाब देना बंद कर चुका CapSkip किसी जॉब को इससे भी ज़्यादा लंबा खींच सकता है।
  • जॉब timeout। वर्कर के timeout ऑप्शन का डिफ़ॉल्ट 60 सेकंड है, और इससे ज़्यादा देर चलने वाली जॉब का वर्कर प्रोसेस kill कर दिया जाता है। जॉब पर ऐसा $timeout सेट करें जो ऊपर के छह मिनट से लंबा हो।
  • retry_after. हर क्यू कनेक्शन किसी reserved जॉब को इतनी देर चलने के बाद क्यू में वापस छोड़ देता है, जो डिफ़ॉल्ट रूप से 90 सेकंड है। अगर जॉब की कोशिशें बाकी हैं, तो एक वर्कर पर अभी भी चल रहा धीमा हल फिर दूसरे वर्कर पर नए सिरे से शुरू हो जाता है, यानी दूसरा हल और दूसरी बार फॉर्म सबमिशन। अगर कोई कोशिश बाकी नहीं है, तो दूसरा वर्कर उसे तुरंत failed मार्क कर देता है, जबकि पहला वर्कर फिर भी फॉर्म सबमिट कर सकता है।

Laravel के दस्तावेज़ कहते हैं कि timeout हमेशा retry_after से कम से कम कई सेकंड छोटा होना चाहिए। तो क्रम यह है: पहले क्लाइंट, फिर जॉब timeout, और आख़िर में retry_after। कैप्चा जॉब को उनका अपना कनेक्शन दें, ताकि लंबा retry_after ऐप की बाकी हर जॉब की रिकवरी को धीमा न करे:

// config/queue.php, under 'connections'
'captcha' => [
    'driver' => 'database',
    'connection' => env('DB_QUEUE_CONNECTION'),
    'table' => env('DB_QUEUE_TABLE', 'jobs'),
    'queue' => 'captcha',
    // Longer than the job's timeout, so no second worker takes it.
    'retry_after' => 420,
    'after_commit' => false,
],

// On the job class
public $timeout = 390;

वर्कर को उसी कनेक्शन और क्यू पर चलाएँ:

php artisan queue:work captcha --queue=captcha

वर्कर के अपने timeout ऑप्शन के बजाय जॉब के $timeout को प्राथमिकता मिलती है, इसलिए कमांड में timeout देने की ज़रूरत नहीं है।

चरण 4: रीट्राई सिर्फ़ उसी की करें जिसे रीट्राई ठीक कर सके

जब तक आप कुछ और न कहें, जॉब को एक ही कोशिश मिलती है, इसलिए कोई भी exception उसे हमेशा के लिए फेल कर देता है। कुछ कैप्चा नाकामियाँ एक और कोशिश के लायक होती हैं: ERROR_CAPTCHA_UNSOLVABLE नतीजा, TimeoutException, या CapSkip के रीस्टार्ट होने की वजह से आया NetworkException। बाकी दोहराने पर कभी सफल नहीं होतीं: गलत API key, गलत बना sitekey (ERROR_GOOGLEKEY), या ऐसा पैरामीटर जिसे क्लाइंट ठुकरा देता है। जिस sitekey को Google मना कर दे, वह ERROR_CAPTCHA_UNSOLVABLE के रूप में लौटता है, इसलिए वह किसी भी दूसरे unsolvable नतीजे की तरह सारी रीट्राई खर्च कर देता है। तीन कोशिशों की अनुमति दें, उनके बीच अंतराल रखें, और जिन मामलों में कोई उम्मीद नहीं, उन्हें तुरंत फेल कर दें।

use CapSkip\Exceptions\ApiException;
use CapSkip\Exceptions\ValidationException;

// On the job class
public $tries = 3;
public $backoff = [30, 120];

// Inside handle(), around the solve
try {
    $token = $solver->recaptcha($m[1], $this->pageUrl)['code'];
} catch (ValidationException $e) {
    $this->fail($e);   // a bad parameter will not fix itself
    return;
} catch (ApiException $e) {
    if (! str_contains($e->getMessage(), 'UNSOLVABLE')) {
        $this->fail($e);   // a wrong API key and the like
        return;
    }
    throw $e;   // retried after the backoff
}

$this->fail() marks the job failed without spending the remaining attempts, and anything rethrown is retried after 30 seconds, then 120. A job killed by its timeout also uses up an attempt, but it is picked up again only when retry_after expires, not after the backoff. A 4xx from the form also throws, and the retry solves and posts again. If the site answers 4xx for bad input, catch Illuminate\Http\Client\RequestException around the post and call $this->fail($e) when $e->response->clientError() is true.

Windows पर वर्कर चलाना

Laravel को CapSkip वाले उसी Windows PC पर चलाना सबसे आसान सेटअप है, और इसमें एक पेच है। Laravel जॉब timeouts को pcntl extension से लागू करता है, और Windows पर pcntl मौजूद ही नहीं है। वहाँ यह bool(false) प्रिंट करता है:

php -r "var_dump(extension_loaded('pcntl'));"

pcntl के बिना जॉब का $timeout और वर्कर का timeout ऑप्शन चुपचाप नज़रअंदाज़ हो जाते हैं, और जॉब तब तक चलती रहती है जब तक वह return न कर दे, चाहे उसमें कितना भी समय लगे। retry_after फिर भी लागू होता है, क्योंकि उसकी जाँच तब होती है जब अगला वर्कर कोई जॉब उठाता है, उसे किसी signal से लागू नहीं किया जाता। यानी Windows पर Laravel में कुछ भी धीमी जॉब को नहीं रोकता। हल को CapSkip के अपने Wait और Row Timeouts और क्लाइंट की 300 सेकंड वाली पोलिंग सीमा ही खत्म करते हैं, इसलिए इन्हें अपनी जगह पर रहने दें। retry_after को उस सबसे लंबे समय से ऊपर रखें जो कोई जॉब ले सकती है: 420 सामान्य रूप से जवाब देने वाले CapSkip के लिए काफ़ी है, और 660 अटकने वाले कनेक्शन को भी कवर करता है, जैसे इंटरनेट के पार Server mode पर।

दो और चीज़ें अलग हैं। Laravel Horizon को pcntl और posix extensions चाहिए, इसलिए वह Windows पर इंस्टॉल नहीं होगा; वहाँ सादा queue:work इस्तेमाल करें, या Horizon को किसी Linux होस्ट पर चलाएँ जो Server mode में CapSkip को कॉल करे। और वहाँ Supervisor नहीं है, इसलिए हर वर्कर को किसी scheduled task या ऐसे service wrapper के तहत चलाएँ जो उसे रीस्टार्ट करे। Linux पर Supervisor का stopwaitsecs आपकी सबसे लंबी जॉब से ज़्यादा होना चाहिए, वरना deploy किसी हल को बीच में ही खत्म कर देता है। दोनों ही मामलों में, deploy या .env बदलने के बाद, अगर आप configuration cache करते हैं तो php artisan config:cache चलाएँ, फिर php artisan queue:restart, क्योंकि वर्कर वही configuration रखता है जिसके साथ वह boot हुआ था।

हर वर्कर प्रोसेस एक बार में एक कैप्चा हल करता है, और CapSkip डिफ़ॉल्ट रूप से एक साथ 10 reCAPTCHA हल तक चलाता है (उसकी reCAPTCHA सेटिंग्स में Max. Threads)। captcha क्यू पर लगभग दस से ज़्यादा वर्कर रखने से जॉब बस CapSkip के अंदर इंतज़ार करती हैं, जहाँ यह समय Wait Timeout में गिना जाता है।

पूरा चलने वाला उदाहरण

<?php

namespace App\Jobs;

use CapSkip\CapSkip;
use CapSkip\Exceptions\ApiException;
use CapSkip\Exceptions\ValidationException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Http;
use RuntimeException;
use Throwable;

class SubmitSignup implements ShouldQueue
{
    use Queueable;

    public $tries = 3;
    public $backoff = [30, 120];
    // GET 30 s + solve up to 300 s + POST 30 s, plus a margin.
    public $timeout = 390;

    public function __construct(public string $pageUrl, public array $fields) {}

    public function handle(CapSkip $solver): void
    {
        $html = Http::timeout(30)->get($this->pageUrl)->throw()->body();
        if (! preg_match('/data-sitekey="([^"]+)"/', $html, $m)) {
            $this->fail(new RuntimeException("No sitekey on {$this->pageUrl}"));
            return;
        }

        try {
            $token = $solver->recaptcha($m[1], $this->pageUrl)['code'];
        } catch (ValidationException $e) {
            $this->fail($e);
            return;
        } catch (ApiException $e) {
            if (! str_contains($e->getMessage(), 'UNSOLVABLE')) {
                $this->fail($e);
                return;
            }
            throw $e;
        }

        // Submit in the same job, while the token is still valid.
        Http::asForm()->timeout(30)
            ->post($this->pageUrl, $this->fields + ['g-recaptcha-response' => $token])
            ->throw();
    }

    public function failed(?Throwable $exception): void
    {
        logger()->warning('Signup gave up', [
            'url' => $this->pageUrl,
            'error' => $exception?->getMessage(),
        ]);
    }
}

इसे चरण 3 वाले captcha कनेक्शन और चरण 1 वाली binding के साथ इस्तेमाल करें, चरण 2 में दिखाए गए तरीके से dispatch करें, और चरण 3 वाली कमांड से एक वर्कर शुरू करें। sitekey वाला pattern double quotes वाले उसी data-sitekey attribute की उम्मीद करता है जो reCAPTCHA का अपना snippet लिखता है। invisible या Enterprise widget के लिए recaptcha() को उससे मेल खाता ऑप्शन पास करें, जैसा reCAPTCHA v2 सॉल्वर पेज पर बताया गया है; इस कॉल के पीछे का raw endpoint यहाँ है: API रेफ़रेंस.

आम errors और उनका मतलब

आप जो देखते हैंकारणफिक्स
60 सेकंड के बाद "has timed out", और जॉब failed मार्क हो जाती हैवर्कर का डिफ़ॉल्ट timeout 60 सेकंड है और जॉब के पास एक ही कोशिश हैजॉब पर $timeout सेट करें, और अगर रीट्राई चाहिए तो $tries भी
फॉर्म दो बार सबमिट होता है, या एक जॉब के लिए दो हल चलते हैंजॉब retry_after (डिफ़ॉल्ट 90 सेकंड) से ज़्यादा देर चली, और किसी दूसरे वर्कर ने उसे उठा लियाकैप्चा जॉब को ऐसे कनेक्शन पर रखें जिसका retry_after, $timeout से लंबा हो
Windows पर जॉब अपने $timeout से काफ़ी आगे तक चलती हैजॉब timeouts को pcntl चाहिए, जो Windows पर नहीं हैCapSkip के अपने timeouts और क्लाइंट की 300 सेकंड वाली सीमा पर भरोसा करें, और retry_after को पूरी जॉब के समय से ऊपर रखें
"has been attempted too many times"उसके वर्कर के मर जाने या retry_after से आगे निकल जाने के बाद जॉब को फिर से उठाया गया, और ऐसा $tries की अनुमति से ज़्यादा बार हुआऐसे वर्कर खोजें जिन्हें किसी deploy ने जॉब के बीच में ही kill कर दिया, और ऐसी जॉब जो retry_after से ज़्यादा चलती हैं
ERROR_CAPTCHA_UNSOLVABLE वाला ApiExceptionCapSkip ने टास्क फेल कर दिया, जैसे जब वह Row Timeout से आगे चला गयाइसे दोबारा throw करें, ताकि Laravel backoff के बाद रीट्राई करे
ERROR_KEY_DOES_NOT_EXIST वाला ApiExceptionCapSkip में API key validation चालू है और CAPSKIP_API_KEY वहाँ की किसी key से मेल नहीं खाताkey ठीक करें और वर्कर रीस्टार्ट करें; जॉब की रीट्राई करने के बजाय उसे फेल कर दें
NetworkException, जो .env बदलने के बाद पहले हल पर आता हैवर्कर अब भी पुराने host के साथ चल रहा है, या CapSkip चल नहीं रहाअगर आप configuration cache करते हैं तो php artisan config:cache चलाएँ, फिर php artisan queue:restart, और Local या Server mode जाँचें
Call to undefined function curl_init()वर्कर जो php.ini लोड करता है, उसमें curl extension बंद हैउसी php.ini में extension=curl चालू करें
जब जॉब "चल रही" होती है, तब पेज रिक्वेस्ट time out हो जाती हैजॉब captcha कनेक्शन के बिना dispatch हुई और QUEUE_CONNECTION, sync है, इसलिए वह वेब रिक्वेस्ट के अंदर ही चलीचरण 2 की तरह captcha कनेक्शन पर dispatch करें, और उस पर एक वर्कर चलाएँ
साइट टोकन ठुकरा देती हैउसे expire होने के बाद सबमिट किया गया, या गलत URL परहल के तुरंत बाद उसी जॉब से, फॉर्म के action URL पर सबमिट करें

FAQ

कैप्चा को controller के अंदर ही हल क्यों न करें?

क्योंकि एक हल में दसियों सेकंड लगते हैं और कई मिनट भी लग सकते हैं। यह उससे ज़्यादा है जितना कोई विज़िटर इंतज़ार करेगा, और उस 30 सेकंड वाले max_execution_time से भी ज़्यादा है जो ज़्यादातर PHP इंस्टॉलेशन किसी वेब रिक्वेस्ट को देते हैं। जॉब को क्यू में डालने से controller तुरंत response लौटा पाता है, जबकि धीमा हिस्सा ऐसे वर्कर प्रोसेस में चलता है जिस पर ऐसी कोई सीमा नहीं होती।

क्या Laravel क्यू कैप्चा जॉब Horizon के तहत चलती है?

हाँ, ऐसे होस्ट पर जिसमें pcntl हो, यानी Linux या macOS। Horizon, config/horizon.php में हर supervisor के लिए timeout सेट करता है, इसलिए captcha क्यू चलाने वाले supervisor को जॉब के 390 सेकंड से थोड़ा ऊपर का timeout दें, जैसे 400, क्योंकि auto balancing strategy के साथ, scale down करते समय Horizon उन वर्करों को रोक देता है जो उसके अपने timeout से ज़्यादा देर चलते हैं। Redis कनेक्शन का retry_after उससे भी ऊपर रखें (420 चलेगा), और CAPSKIP_HOST को उस Windows मशीन पर लगाएँ जो Server mode में CapSkip चला रही है।

क्या Linux सर्वर पर चल रहा Laravel ऐप मेरे Windows PC पर चल रहे CapSkip का इस्तेमाल कर सकता है?

हाँ। कनेक्शन सेटिंग्स में CapSkip को Server mode पर कर दें ताकि वह किसी नेटवर्क पते पर सुने, ऐप की .env में CAPSKIP_HOST सेट करें, और वर्कर रीस्टार्ट करें। अगर रास्ता इंटरनेट से होकर जाता है, तो स्टैटिक पब्लिक IP और ऐसा फ़ायरवॉल नियम इस्तेमाल करें जो सिर्फ़ आपके सर्वर को आने दे, और API key validation चालू करें। सॉल्वर आपकी अपनी Windows मशीन पर ही रहता है, इसलिए हल की गिनती के तरीके में कुछ नहीं बदलता।

Hangfire या Celery की तुलना में यह कैसा है?

ढाँचा हर जगह एक जैसा है: एक ही जॉब में हल और सबमिट करें, सिर्फ़ उन्हीं नाकामियों की रीट्राई करें जिन्हें रीट्राई ठीक कर सके, और वर्करों की संख्या CapSkip के threads की गिनती के हिसाब से रखें। फ़र्क इसमें है कि कौन सा डिफ़ॉल्ट आपको फँसाता है। Laravel एक ही कोशिश देता है और धीमी जॉब को दूसरे वर्कर को सौंप देता है, जो उसे फेल कर देता है या, रीट्राई की अनुमति देने पर, उसे फिर से चला देता है; Hangfire घंटों में दस बार रीट्राई करता है। .NET वाला संस्करण यहाँ है: Hangfire कैप्चा जॉब गाइड.

संक्षेप में

Laravel क्यू कैप्चा जॉब के लिए, CapSkip क्लाइंट को container में bind करें, और हल व सबमिट एक ही जॉब में करें। कैप्चा जॉब को 420 सेकंड के retry_after वाला उनका अपना कनेक्शन दें, backoff के साथ $timeout को 390 पर और $tries को 3 पर सेट करें, और जिन errors को रीट्राई ठीक नहीं कर सकती, उन पर जॉब फेल कर दें। Windows पर याद रखें कि pcntl के बिना कोई timeout लागू नहीं होता, और हर deploy के बाद वर्कर रीस्टार्ट करें।

इन सेटिंग्स का असली फ़ायदा रीट्राई में मिलता है। जब कैप्चा सॉल्वर आपकी अपनी मशीन पर चलता है, तो किसी unsolvable कैप्चा की दो बार रीट्राई करने वाली जॉब एक आम बात है, क्योंकि वहाँ हर अतिरिक्त कोशिश की कीमत बस वर्कर और thread का समय है, मीटर किया गया कोई और हल नहीं।