Lost Art of Troubleshooting

A presentation at DevOpsDays Boston 2017 in September 2017 in Boston, MA, USA by Leon Fayer

Slide 1

Slide 1

h t t p : / / f a y e r p l a y. c o m Leon Fayer lost art of trouble sho ot in g @papa_fire

Slide 2

Slide 2

{me} 20+ years breaking & fixing dev, architect, [devops] vp @ OmniTI fix other people’s @papa_fire

Slide 3

Slide 3

MID ATLANTIC DEV CON Cloud — Mobile — Web — Dev Thanks to Our Sponsors @papa_fire

Slide 4

Slide 4

questions & comments are welcome! https://joind.in/talk/94a2c @papa_fire

Slide 5

Slide 5

why troubleshooting? @papa_fire

Slide 6

Slide 6

cloud ruined everything it really did @papa_fire

Slide 7

Slide 7

when in doubt - reboot DevOps mantra for managing cloud-based systems 2018 1998 Most reliable way to fix Windows problems destroy and rebuild

Slide 8

Slide 8

old McDonald had a farm

Slide 9

Slide 9

old McDonald lost a farm due to mad cow disease

Slide 10

Slide 10

troubleshooting - a form of problem solving @papa_fire

Slide 11

Slide 11

problem solving - ability to fix things that you know nothing about @papa_fire

Slide 12

Slide 12

why is problem solving important? @papa_fire

Slide 13

Slide 13

… because systems are complex @papa_fire

Slide 14

Slide 14

… because of Murphy’s law @papa_fire

Slide 15

Slide 15

… because someone is always watching @papa_fire

Slide 16

Slide 16

your green field coding skills are about as useful as your Renaissance Art degree @mipsytipsy @papa_fire

Slide 17

Slide 17

{disclamer} @papa_fire

Slide 18

Slide 18

@papa_fire

Slide 19

Slide 19

wishful thinking @papa_fire

Slide 20

Slide 20

reality @papa_fire

Slide 21

Slide 21

where to begin? @papa_fire

Slide 22

Slide 22

replicate @papa_fire

Slide 23

Slide 23

bob: I fixed it me: how do you know? bob: it ran fine after the fix me: did it run before the fix? bob: … @papa_fire

Slide 24

Slide 24

OUR TEAM isolate @papa_fire

Slide 25

Slide 25

Logins aren’t working 100% of the time, alerts are going off periodically, and today the system didn’t send the scheduled emails @papa_fire

Slide 26

Slide 26

Logins aren’t working 100% of the time, alerts are going off periodically, and today the system didn’t send the scheduled emails @papa_fire

Slide 27

Slide 27

Logins aren’t working 100% of the time, alerts are going off periodically, and today the system didn’t send the scheduled emails @papa_fire

Slide 28

Slide 28

fix? @papa_fire

Slide 29

Slide 29

what’s the problem? it’s broken! @papa_fire

Slide 30

Slide 30

understanding

Slide 31

Slide 31

OUR TEAM @papa_fire understand problem

Slide 32

Slide 32

“ we can’t support 100s req/min we need to scale better! @papa_fire

Slide 33

Slide 33

“ we can’t support 100s req/min we need to scale better! improve performance @papa_fire

Slide 34

Slide 34

performance problem @papa_fire

Slide 35

Slide 35

perceived problem @papa_fire

Slide 36

Slide 36

actual problem @papa_fire

Slide 37

Slide 37

OUR TEAM @papa_fire understand business

Slide 38

Slide 38

“ I don’t give a **** if the datacenter is on fire as long as I am still making money @papa_fire

Slide 39

Slide 39

what does it mean to you? @papa_fire

Slide 40

Slide 40

@papa_fire

Slide 41

Slide 41

sales @papa_fire

Slide 42

Slide 42

@papa_fire

Slide 43

Slide 43

content @papa_fire

Slide 44

Slide 44

ad revenue content @papa_fire

Slide 45

Slide 45

every technical decision powers a business need @papa_fire

Slide 46

Slide 46

i don’t get paid for this @papa_fire

Slide 47

Slide 47

i get paid for this @papa_fire

Slide 48

Slide 48

OUR TEAM @papa_fire understand impact

Slide 49

Slide 49

@papa_fire

Slide 50

Slide 50

is there a lesser of two evils?

Slide 51

Slide 51

sometimes breaking = fixing @papa_fire

Slide 52

Slide 52

time is money @papa_fire

Slide 53

Slide 53

MTTR @papa_fire

Slide 54

Slide 54

80% now > 100% tomorrow @papa_fire

Slide 55

Slide 55

incremental improvements @papa_fire

Slide 56

Slide 56

anatomy of a problem @papa_fire

Slide 57

Slide 57

anatomy of a problem problem norm @papa_fire norm

Slide 58

Slide 58

anatomy of a problem problem acceptable norm @papa_fire norm

Slide 59

Slide 59

anatomy of a problem problem acceptable norm norm fix @papa_fire fix fix fix

Slide 60

Slide 60

MTTR @papa_fire

Slide 61

Slide 61

@papa_fire understanding of what have we learned? what’s important cause and effect largest impact acceptable risk

Slide 62

Slide 62

what not to do @papa_fire

Slide 63

Slide 63

don’t assume @papa_fire

Slide 64

Slide 64

Spurious Correlations: http://www.tylervigen.com/spurious-correlations @papa_fire

Slide 65

Slide 65

@papa_fire

Slide 66

Slide 66

don’t trust errors @papa_fire

Slide 67

Slide 67

your birthdate has changed @papa_fire

Slide 68

Slide 68

@papa_fire

Slide 69

Slide 69

it’s not documented @papa_fire

Slide 70

Slide 70

I didn’t build it @papa_fire

Slide 71

Slide 71

it passed all the tests @papa_fire

Slide 72

Slide 72

everything looks right @papa_fire

Slide 73

Slide 73

don’t give up @papa_fire

Slide 74

Slide 74

@papa_fire

Slide 75

Slide 75

solve the problem don’t feed your ego @papa_fire

Slide 76

Slide 76

ask for help @papa_fire

Slide 77

Slide 77

OUR TEAM @papa_fire tools

Slide 78

Slide 78

logging monitoring profiling @papa_fire

Slide 79

Slide 79

actionable concise logging parsable @papa_fire

Slide 80

Slide 80

log levels @papa_fire trace debug info notice warn error fatal

Slide 81

Slide 81

production log levels @papa_fire trace debug info notice warn error fatal

Slide 82

Slide 82

OUR TEAM @papa_fire [2017-02-01 16:46:31] Queuing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 16:46:31] AbandonedReservation successfully enqueued. [2017-02-01 18:57:02] Processing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 18:57:02] Parsed args [2017-02-01 18:57:02] Posting to API [2017-02-01 18:57:02] Initializing args [2017-02-01 18:57:02] Loading reservation_form_data [2017-02-01 18:57:03] Reservation Form Data loaded successfully [2017-02-01 18:57:03] Appending campaign info [2017-02-01 18:57:03] Reservation name: [Some very very very long name] [2017-02-01 18:57:03] Using code = SUPERHERO:20171007 for this instance. [2017-02-01 18:57:03] Setting currency to US Dollar [2017-02-01 18:57:03] Appending marketing info [2017-02-01 18:57:03] Have a non-sku source_code [2017-02-01 18:57:03] Marketing: setting campaignid = GOOGLE [2017-02-01 18:57:03] Appending match rule = Match Rule [2017-02-01 18:57:03] Appending user info [2017-02-01 18:57:03] Appending order info [2017-02-01 18:57:03] Fetching cost range for item_id = 975, sku_id = 4871 [2017-02-01 18:57:03] Determining actual cost table [2017-02-01 18:57:03] Appending comment notes [2017-02-01 18:57:03] Appending abandoned flag [2017-02-01 18:57:03] API GET data: { token = gEcre26reWrAdEnufE3HesVupRepahuDumapHuyap2evufreWrufraBebre7u4a6 contact.address1_city = New York contact.address1_country = USA contact.address1_line1 = 123 test lane contact.address1_line2 = contact.address1_postalcode = 12345 contact.address1_stateorprovince = contact.emailaddress1 = joe.smith@gmail.com contact.firstname = joe contact.lastname = smith contact.mobilephone = 1234567890 … } [2017-02-01 19:04:03] Post complete, took 420 seconds [2017-02-01 19:04:03] ERROR: Curl returned unsuccessfully with return code 28 (Timeout was reached) [2017-02-01 19:04:04] Finishing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0

Slide 83

Slide 83

[2017-02-01 16:46:31] Queuing FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 16:46:31] Queuing UUID: [2017-02-01 16:46:31] AbandonedReservation successfully enqueued. [2017-02-01 18:57:02] Processing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 18:57:02] Parsed args [2017-02-01 18:57:02] Posting to API [2017-02-01 18:57:02] Initializing args [2017-02-01 18:57:02] Loading reservation_form_data [2017-02-01 18:57:03] Reservation Form Data loaded successfully [2017-02-01 18:57:03] Appending campaign info [2017-02-01 18:57:03] Reservation name: [Some very very very long name] [2017-02-01 18:57:03] Using code = SUPERHERO:20171007 for this instance. [2017-02-01 18:57:03] Setting currency to US Dollar [2017-02-01 18:57:03] Appending marketing info [2017-02-01 18:57:03] Have a non-sku source_code [2017-02-01 18:57:03] Marketing: setting campaignid = GOOGLE [2017-02-01 18:57:03] Appending match rule = Match Rule [2017-02-01 18:57:03] Appending user info [2017-02-01 18:57:03] Appending order info [2017-02-01 18:57:03] Fetching cost range for item_id = 975, sku_id = 4871 [2017-02-01 18:57:03] Determining actual cost table [2017-02-01 18:57:03] Appending comment notes [2017-02-01 18:57:03] Appending abandoned flag [2017-02-01 18:57:03] API GET data: { token = gEcre26reWrAdEnufE3HesVupRepahuDumapHuyap2evufreWrufraBebre7u4a6 contact.address1_city = New York contact.address1_country = USA contact.address1_line1 = 123 test lane contact.address1_line2 = contact.address1_postalcode = 12345 contact.address1_stateorprovince = contact.emailaddress1 = joe.smith@gmail.com contact.firstname = joe contact.lastname = smith contact.mobilephone = 1234567890 … } [2017-02-01 19:04:03] Post complete, took 420 seconds [2017-02-01 19:04:03] ERROR: Curl returned unsuccessfully with return code 28 (Timeout was reached) [2017-02-01 19:04:04] Finishing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 OUR TEAM useful information [2017-02-01 18:57:02] Processing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 18:57:03] API GET data: [2017-02-01 19:04:03] Post complete, took 420 seconds @papa_fire [2017-02-01 19:04:04] Finishing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0

Slide 84

Slide 84

[2017-02-01 16:46:31] Queuing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 16:46:31] AbandonedReservation successfully enqueued. [2017-02-01 18:57:02] Processing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 18:57:02] Parsed args [2017-02-01 18:57:02] Posting to API [2017-02-01 18:57:02] Initializing args [2017-02-01 18:57:02] Loading reservation_form_data [2017-02-01 18:57:03] Reservation Form Data loaded successfully [2017-02-01 18:57:03] Appending campaign info [2017-02-01 18:57:03] Reservation name: [Some very very very long name] [2017-02-01 18:57:03] Using code = SUPERHERO:20171007 for this instance. [2017-02-01 18:57:03] Setting currency to US Dollar [2017-02-01 18:57:03] Appending marketing info [2017-02-01 18:57:03] Have a non-sku source_code [2017-02-01 18:57:03] Marketing: setting campaignid = GOOGLE [2017-02-01 18:57:03] Appending match rule = Match Rule [2017-02-01 18:57:03] Appending user info [2017-02-01 18:57:03] Appending order info [2017-02-01 18:57:03] Fetching cost range for item_id = 975, sku_id = 4871 [2017-02-01 18:57:03] Determining actual cost table [2017-02-01 18:57:03] Appending comment notes [2017-02-01 18:57:03] Appending abandoned flag [2017-02-01 18:57:03] API GET data: { token = gEcre26reWrAdEnufE3HesVupRepahuDumapHuyap2evufreWrufraBebre7u4a6 contact.address1_city = New York contact.address1_country = USA contact.address1_line1 = 123 test lane contact.address1_line2 = contact.address1_postalcode = 12345 contact.address1_stateorprovince = contact.emailaddress1 = joe.smith@gmail.com contact.firstname = joe contact.lastname = smith contact.mobilephone = 1234567890 … } [2017-02-01 19:04:03] Post complete, took 420 seconds [2017-02-01 19:04:03] ERROR: Curl returned unsuccessfully with return code 28 (Timeout was reached) [2017-02-01 19:04:04] Finishing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 OUR TEAM information I need [2017-02-01 18:57:02] Processing UUID: FC0470D4-E19D-11E6-8FB4-CB1814EF18C0 [2017-02-01 19:04:03] ERROR: Curl returned unsuccessfully with return code 28 (Timeout was reached) @papa_fire

Slide 85

Slide 85

verbosity is expensive @papa_fire 2k log/req * 100 req/sec * 60 sec/min * 2 webservers

Slide 86

Slide 86

all inclusive business-first monitoring correlatable @papa_fire

Slide 87

Slide 87

“ in God we trust, all others we monitor @papa_fire

Slide 88

Slide 88

“ in God we trust, all others* we monitor * systems, code, business, marketing, users, databases, performance … @papa_fire

Slide 89

Slide 89

OUR TEAM why monitor? (we have tests and logs) @papa_fire

Slide 90

Slide 90

OUR TEAM because things change @papa_fire

Slide 91

Slide 91

OUR TEAM because things change @papa_fire in production

Slide 92

Slide 92

outside of our control OUR TEAM because things change @papa_fire in production

Slide 93

Slide 93

‣ online marketing company ‣ major e-commerce component ‣ ~100 million users ‣ 1 billion emails/month ‣ 300,000 lines of code @papa_fire ‣ 5600 metrics collected

Slide 94

Slide 94

what’s the problem? it’s broken! @papa_fire

Slide 95

Slide 95

revenue @papa_fire

Slide 96

Slide 96

revenue @papa_fire

Slide 97

Slide 97

revenue user performance @papa_fire

Slide 98

Slide 98

revenue user performance @papa_fire database load

Slide 99

Slide 99

revenue email bounce rate user performance @papa_fire database load

Slide 100

Slide 100

OUR TEAM don’t underestimate correlation @papa_fire

Slide 101

Slide 101

profiling @papa_fire

Slide 102

Slide 102

OUR TEAM when you have the “what” but still have no idea “why” @papa_fire

Slide 103

Slide 103

#!/usr/sbin/dtrace -s #pragma quiet ::ap_process_request:process-request-entry /zonename == “www4”/ { self->uri = copyinstr(arg1); self->runtime = 0; self->waittime = 0; self->oncpu = timestamp; } OUR TEAM sched:::off-cpu /self->uri != 0/ { self->runtime += timestamp - self->oncpu; self->offcpu = timestamp; } TOTAL TIME SPENT OFF-CPU BY ALL HITS TO THIS URL sched:::on-cpu /self->uri != 0/ { self->oncpu = timestamp; self->waittime += timestamp - self->offcpu; } ::ap_process_request:process-request-return /self->uri != 0/ { @duration[self->uri] = sum(self->runtime); @waiting[self->uri] = sum(self->waittime); @count[self->uri] = count(); } :::tick-5min { printf(“\n%Y\n”, walltimestamp); printf(“\nTOTAL TIME SPENT ON CPU BY ALL HITS trunc(@duration,10); printa(@duration); trunc(@duration); ON THIS URL\n”); printf(“\n\nNUMBER OF HITS\n”); trunc(@count,10); printa(@count); trunc(@count); printf(“\n\nTOTAL TIME SPENT OFF-CPU BY ALL HITS TO THIS URL\n”); trunc(@waiting,10); printa(@waiting); trunc(@waiting); } @papa_fire /directory /api/map_search /api/mobile/get_all_items /m/ /m/directory /api/mobile/get_profile /api/holiday_feed /api/mobile/get_all_widgets /api/get_item/60693 /m/events/all /api/all_items /api/mobile/get_all_events 6850049 7341249 7980925 9124747 9175345 11729556 12603853 15043481 19773404 26165132 27362330 368584344

Slide 104

Slide 104

#!/usr/sbin/dtrace -s #pragma quiet ::ap_process_request:process-request-entry /zonename == “www4”/ { self->uri = copyinstr(arg1); self->runtime = 0; self->waittime = 0; self->oncpu = timestamp; } OUR TEAM sched:::off-cpu /self->uri != 0/ { self->runtime += timestamp - self->oncpu; self->offcpu = timestamp; } TOTAL TIME SPENT OFF-CPU BY ALL HITS TO THIS URL sched:::on-cpu /self->uri != 0/ { self->oncpu = timestamp; self->waittime += timestamp - self->offcpu; } ::ap_process_request:process-request-return /self->uri != 0/ { @duration[self->uri] = sum(self->runtime); @waiting[self->uri] = sum(self->waittime); @count[self->uri] = count(); } :::tick-5min { printf(“\n%Y\n”, walltimestamp); printf(“\nTOTAL TIME SPENT ON CPU BY ALL HITS trunc(@duration,10); printa(@duration); trunc(@duration); ON THIS URL\n”); printf(“\n\nNUMBER OF HITS\n”); trunc(@count,10); printa(@count); trunc(@count); printf(“\n\nTOTAL TIME SPENT OFF-CPU BY ALL HITS TO THIS URL\n”); trunc(@waiting,10); printa(@waiting); trunc(@waiting); } @papa_fire /directory /api/map_search /api/mobile/get_all_items /m/ /m/directory /api/mobile/get_profile /api/holiday_feed /api/mobile/get_all_widgets /api/get_item/60693 /m/events/all /api/all_items /api/mobile/get_all_events /api/mobile/get_all_events 6850049 7341249 7980925 9124747 9175345 11729556 12603853 15043481 19773404 26165132 27362330 368584344 368584344

Slide 105

Slide 105

down the rabbit hole @papa_fire

Slide 106

Slide 106

OUR TEAM @papa_fire

Slide 107

Slide 107

unreserve.php 81.60% OUR TEAM @papa_fire

Slide 108

Slide 108

unreserve.php 81.60% OUR TEAM headerdr.inc @papa_fire

Slide 109

Slide 109

unreserve.php 81.60% OUR TEAM headerdr.inc header.inc @papa_fire

Slide 110

Slide 110

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT(*) AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file run successfully’.”\n\n”,FILE_APPEND); @papa_fire

Slide 111

Slide 111

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); SELECT id, CID AS cid FROM cart WHERE timestamp < :time foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT(*) AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); @papa_fire

Slide 112

Slide 112

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT() AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); foreach($returns as $item) { SELECT COUNT() AS count FROM orders WHERE sid = :sid @papa_fire

Slide 113

Slide 113

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT() AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); foreach($returns as $item) { SELECT COUNT() AS count FROM orders WHERE sid = :sid UPDATE items SET reserved = ” WHERE id = :id @papa_fire

Slide 114

Slide 114

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT() AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); foreach($returns as $item) { SELECT COUNT() AS count FROM orders WHERE sid = :sid UPDATE items SET reserved = ” WHERE id = :id DELETE FROM items WHERE id = :id @papa_fire

Slide 115

Slide 115

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. TODO: Use an exists() or something to collapse the $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); two SELECTs. foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT(*) AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); @papa_fire

Slide 116

Slide 116

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT(*) AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); mail(‘foo@bar.com’,’Unreserve Cron’, ‘Cron is running successfully on production.<\n>’); @papa_fire

Slide 117

Slide 117

// ——- Find items on reserve past cutoff ——# TODO: Use an exists() or something to collapse the two SELECTs. $cutoff = date(‘Y-m-d H:i:s’, (time() - (12 * 3600))); $sql = “SELECT id, CID AS cid FROM cart WHERE timestamp < :time”; $returns = $dbh->fetchAll($sql, array(‘time’ => $cutoff)); foreach($returns as $item) { // ——- Check for completed order ——$row = $dbh->fetchRow(“SELECT COUNT(*) AS count FROM orders WHERE sid = :sid”, array(‘sid’ => $item[‘cid’])); if($row[‘count’] == 0) { // ——- Return items to inventory ——$dbh->execute(“UPDATE items SET reserved = ” WHERE id = :id”, array(‘id’ => $item[‘id’])); $dbh->execute(“DELETE FROM items WHERE id = :id”, array(‘id’ => $item[‘id’])); } } mail(‘foo@bar.com’,’Unreserve Cron ‘,’Cron has running successfully on production.<\n>’); $log_unreserve = “logs/unreserve_chk.log”; file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); file_put_contents($log_unreserve,”\n”.date(“m-d-Y h:i:s”).’ : Unreserve file ran successfully’.”\n\n”,FILE_APPEND); @papa_fire

Slide 118

Slide 118

@papa_fire required skill troubleshooting is … educational iterative frustrating rewarding

Slide 119

Slide 119

@papa_fire

Slide 120

Slide 120

questions? https://joind.in/talk/94a2c @papa_fire