<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4471109663192942674</id><updated>2012-01-12T08:21:40.232-06:00</updated><category term='deadlocks'/><category term='indexes'/><category term='Introduction'/><category term='Interests'/><category term='mysql'/><category term='java'/><category term='complaining'/><category term='leaky'/><category term='books'/><category term='abstraction'/><category term='SQL Server'/><category term='reading list'/><category term='api design'/><category term='opinions'/><category term='databases'/><title type='text'>Many Cups of Coffee</title><subtitle type='html'>Discussion of software development pragmatics in the space of java enterprise development, the confluence of errors that inevitably plague such projects, and the many cups of coffee required to make it through the day.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-3623214924238947736</id><published>2012-01-12T01:41:00.000-06:00</published><updated>2012-01-12T01:42:06.602-06:00</updated><title type='text'>Multi-tier application + database deadlock or why databases aren't queues (part1)</title><content type='html'>Databases aren't queues.&lt;br /&gt;&lt;br /&gt;And despite the ubiquitous presence of queuing technology out there (ActiveMQ, MSMQ, MSSQL Service Broker, Oracle Advanced Queuing) there are plenty of times when we ask our relational brethren to pretend to be queues.&amp;nbsp; This is the story of one such folly, and along the way, we'll delve into some interesting sub-plots of deadlocks, lock escalation, execution plans, and covering indexes, oh my!&amp;nbsp; Hopefully we'll laugh, we'll cry, and get the bad guy in the end (turned out I was the bad guy).&lt;br /&gt;&lt;br /&gt;This is &lt;b&gt;part one&lt;/b&gt; of a multi-part series describing the whole saga.  In this part, I lay out the problem, the initial symptom, and the tools and commands I used to figure out what was going wrong.&lt;br /&gt;&lt;h3&gt;And so it starts...&lt;/h3&gt;I'm going to set the stage for our discussion, to introduce you to the problem, and establish the characters involved in our tragedy.  Let's say that this system organizes music CDs into labeled buckets.  A CD can only be in one bucket at a time, and the bucket tracks at an aggregate level how many CDs are contained within it (e.g. bucket "size").  You can visualize having a stack of CDs and two buckets: "good CDs" and "bad CDs".  Every once in a while you decide that you don't like your bucket choices, and you want to redistribute the CDs into new buckets--perhaps by decade: "1980s music", "1990s music", "all other (inferior) music".  Later you might change your mind again and come up with a new way to organize your CDs, etc.  We will call each "set" of buckets a "generation".  So at generation zero you had 2 buckets "good CDs" and "bad CDs", at generation one you had "1980s CDs", etc, and so on and so on.  The generation always increases over time as you redistribute your CDs from a previous generation's buckets to the next generation's buckets.&lt;br /&gt;&lt;br /&gt;Lastly, while I might have my music collection organized in some bucket scheme, perhaps my friend Jerry has his own collection and his own bucket scheme.  So entire sets of buckets over generations can be grouped into music &lt;i&gt;collections&lt;/i&gt;.  Collections are completely independent: Jerry and I don't share CDs nor do we share buckets. &amp;nbsp;We just happen to be able to use the same system to manage our music collections.&lt;br /&gt;&lt;br /&gt;So we have:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;CDs -- the things which are contained within buckets, that we redistribute for every new generation&lt;/li&gt;&lt;li&gt;Buckets -- the organizational units, grouped by generation, which contain CDs.  Each bucket has a sticky note on it with the number of CDs currently in the bucket.&lt;/li&gt;&lt;li&gt;Generation -- the set of buckets at a particular point in time.&lt;/li&gt;&lt;li&gt;Collection -- independent set of CDs and buckets&lt;/li&gt;&lt;/ul&gt;Even while we're redistributing the CDs from one generation's buckets to the next, a CD is only in one bucket at a time.  Visualize physically moving the CD from "good CDs" (generation 0) to "1980s music" (generation 1).&lt;br /&gt;&lt;br /&gt;&lt;i&gt;NOTE: Our actual system has nothing to do with CDs and buckets-- I just found it easier to map the system into this easy to visualize metaphor.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;In this system we have millions of CDs, thousands of buckets, and lots of CPUs moving CDs from bins in one generation to the next (parallel, but not distributed).  The size of each bucket &lt;i&gt;must&lt;/i&gt; be consistent at any point in time.&lt;br /&gt;&lt;br /&gt;So assume the database model looks something like:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Buckets&lt;/b&gt; Table&lt;/li&gt;&lt;ul&gt;&lt;li&gt;bucketId &lt;i&gt;(e.g. 1,2,3,4)&lt;/i&gt; - &lt;b&gt;PRIMARY KEY CLUSTERED&lt;/b&gt;&lt;/li&gt;&lt;li&gt;name &lt;i&gt;(e.g. 80s music, 90s music)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;generation &lt;i&gt;(e.g. 0, 1, 2)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;size &lt;i&gt;(e.g. 4323, 122)&lt;/i&gt;&lt;/li&gt;&lt;li&gt;collectionId &lt;i&gt;(e.g. "Steves Music Collection")&lt;/i&gt; - &lt;b&gt;NON-CLUSTERED INDEX&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;&lt;b&gt;Cds&lt;/b&gt; Table&lt;/li&gt;&lt;ul&gt;&lt;li&gt;cdId &lt;i&gt;(e.g. 1,2,3,4)&lt;/i&gt; - &lt;b&gt;PRIMARY KEY CLUSTERED&lt;/b&gt;&lt;/li&gt;&lt;li&gt;name &lt;i&gt;(e.g. "Modest Mouse - Moon and Antarctica", "Interpol - Antics")&lt;/i&gt;&lt;/li&gt;&lt;li&gt;bucketId &lt;i&gt;(e.g. 1,2, etc. foreign key to the Bucket table)&lt;/i&gt; - &lt;b&gt;NON-CLUSTERED INDEX&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;Note that both tables are clustered by their primary keys-- this means that the actual record data itself is stored in the leaf nodes of the primary index. &amp;nbsp;I.e. the table itself is an index. &amp;nbsp;In addition, Buckets can be looked up by "music collection" without scanning (see the secondary, non-clustered index on collectionId), and Cds can be looked up by bucketId without scanning (see the secondary, non-clustered index on Cds.bucketId).&lt;br /&gt;&lt;h3&gt;The algorithm&lt;/h3&gt;So I wrote the redistribution process with a few design goals: (1) it needed to work online.  I could concurrently add new CDs into the current generations bins while redistributing. (2) I could always locate a CD-- i.e. I could never falsely report that some CD was missing just because I happen to search during a redistribution phase. (3) if we interrupt the redistribution process, we can resume it later. (4) it needed to be parallel.&amp;nbsp;I wanted to accomplish (1) and (2) with bounded blocking time so whatever blocking work I needed to do, I wanted it to be as short as possible to increase concurrency.&lt;br /&gt;&lt;br /&gt;I used a simple concurrency abstraction that hosted a pool of workers who shared a supplier of work.  The supplier of work would keep giving "chunks" of items to move from one bucket to another.  We only redistribute a single &lt;i&gt;music collection&lt;/i&gt; at a time. The supplier was shared by all of the workers, but it was synchronized for safe multi-threaded access.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The algorithm for each worker is like:&lt;br /&gt;&lt;pre&gt;(I)   Get next chunk of work&lt;br /&gt;(II)  For each CD decide the new generation bucket in which it belongs&lt;br /&gt;        (accumulating the size deltas for old buckets and new buckets)&lt;br /&gt;(III) Begin database transaction&lt;br /&gt;(IV)   Flush accumulated size deltas for buckets&lt;br /&gt;(V)    Flush foreign key updates for CDs to put them in new buckets&lt;br /&gt;(VI)  Commit database transaction&lt;/pre&gt;&lt;pre&gt;&lt;/pre&gt;&lt;br /&gt;Each worker would be given a chunk of CDs for the current music collection that was being redistributed (I).  The worker would do some work to decide which bucket in the &lt;i&gt;new&lt;/i&gt; generation should get the CD (II).  The worker would accumulate the deltas for counts: decrementing from the original bucket and incrementing the count for the new bucket.  Then the worker would flush (IV) these deltas in a correlated update like &lt;code&gt;UPDATE Buckets SET size = size + 123 WHERE bucketId = 1&lt;/code&gt;.  After the size updates were flushed, it would then flush (V) all of the individual updates to the foreign key fields to refer to the new generations buckets like &lt;code&gt;UPDATE Cds SET bucketId = 123 WHERE bucketId = 101&lt;/code&gt;.  These two operations happen in the same database transaction.&lt;br /&gt;&lt;br /&gt;The supplier that &lt;i&gt;gives&lt;/i&gt; work to the workers is a typical "queue" like SELECT query -- we want to iterate over all of the items in the music collection in the old generation.  This happens in a &lt;i&gt;separate&lt;/i&gt; connection, separate database transaction from the workers (discussed later).  The next chunk will be read using the worker thread (with thread safe synchronization). &amp;nbsp;This separate "reader" connection doesn't have its own thread or anything.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;First sign of trouble - complete stand still&lt;/h3&gt;So we were doing some large volume testing on not-so-fast hardware, when suddenly...the system just came to a halt.  We seemed to be in the middle of moving CDs to new buckets, and they just stopped making progress.&lt;br /&gt;&lt;h5&gt;Finding out what the Java application was doing&lt;/h5&gt;So first step was to see what the Java application was doing:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;c:\&amp;gt;jps&lt;br /&gt;1234 BucketRedistributionMain&lt;br /&gt;3456 jps&lt;br /&gt;c:\&amp;gt;jstack 1234 &amp;gt; threaddump.out&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;Jps&lt;/b&gt; finds the java process id, and then run &lt;b&gt;jstack&lt;/b&gt; to output a stacktrace for each of the threads in the java program.  Jps and Jstack are included in the JDK.&lt;br /&gt;&lt;br /&gt;The resulting stack trace showed that all workers were waiting in &lt;b&gt;socketRead&lt;/b&gt; to complete the database update to flush the bucket size updates (step IV above).&lt;br /&gt;&lt;br /&gt;Here is the partial stack trace for one of the workers (some uninteresting frames omitted for brevity):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;"container-lowpool-3" daemon prio=2 tid=0x0000000007b0e000 nid=0x4fb0 runnable [0x000000000950e000]&lt;br /&gt;   java.lang.Thread.State: RUNNABLE&lt;br /&gt; at java.net.SocketInputStream.socketRead0(Native Method)&lt;br /&gt; at java.net.SocketInputStream.read(Unknown Source)&lt;br /&gt; ...&lt;br /&gt; at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(Unknown)&lt;br /&gt; at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(Unknown)&lt;br /&gt; ...&lt;br /&gt; at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(Unknown)&lt;br /&gt; ...&lt;br /&gt; at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)&lt;br /&gt; ...&lt;br /&gt; at com.mycompany.BucketRedistributor$Worker.updateBucketSizeDeltas()&lt;br /&gt; ...&lt;br /&gt; at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)&lt;br /&gt; ...&lt;br /&gt; at java.lang.Thread.run(Unknown Source)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see our worker was updating the bucket sizes which resulted in a Hibernate "flush" (actually pushes the database query across the wire to the database engine), and then we await the response packet from MSSQL once the statement is complete.  Note that we are using the jtds MSSQL driver (as evidenced by the net.sourceforge.jtds in the stack trace.  &lt;br /&gt;&lt;br /&gt;So the next question is &lt;i&gt;why&lt;/i&gt; is the database just hanging out doing nothing?&lt;br /&gt;&lt;h5&gt;Finding out what the database was doing...&lt;/h5&gt;MSSQL provides a lot of simple ways to get insight into what the database is doing.  First let's see the state of all of the connections.  Open SQL Server Management Studio (SSMS), click New Query, and type &lt;code&gt;exec sp_who2&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This will return output that looks like:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-N3MdPdtexmg/Tw53LbINicI/AAAAAAAABDE/5-cfk-PB43o/s1600/blog_sp_who2_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="133" src="http://3.bp.blogspot.com/-N3MdPdtexmg/Tw53LbINicI/AAAAAAAABDE/5-cfk-PB43o/s400/blog_sp_who2_1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;You can see all of the &lt;b&gt;spids&lt;/b&gt; for sessions to the database.  There should be five that we're are interested in: one for the "work queue" query and four for the workers to perform updates.  The sp_who2 output includes a &lt;b&gt;blkBy&lt;/b&gt; column which shows the spid that is &lt;i&gt;blocking&lt;/i&gt; the given spid in the case that the given spid is SUSPENDED.&lt;br /&gt;&lt;br /&gt;We can see that spid 56 is the "work queue" SELECT query (highlighted red).  Notice that no one is blocking it... then we see spids 53, 54, 60, and 61 (highlighted in yellow) that are all waiting on 56 (or each other).  Disregard 58 - its application source is the management studio as you can see.  &lt;br /&gt;&lt;br /&gt;So how curious! the reader query is blocking all of the update workers and preventing them from pushing their size updates.  The reader "work queue" query looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SELECT c.cdId, c.bucketId FROM Buckets b INNER JOIN Cds c ON b.bucketId = c.bucketId WHERE b.collection = 'Steves Collection' and b.generation = 23&lt;br /&gt;&lt;/pre&gt;&lt;h5&gt;Investing blocking and locking problems...&lt;/h5&gt;I see that spid 56 is blocking everyone else.  So what locks is 56 holding?  In a new query window, I ran &lt;code&gt;exec sp_lock 56&lt;/code&gt; and &lt;code&gt;exec sp_lock 53&lt;/code&gt; to see which locks each was holding and who was waiting on what.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-PtTRd7A5BHk/Tw587Bu7HDI/AAAAAAAABDQ/qMyHZQnsjuE/s1600/blog_sp_lock_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="205" src="http://4.bp.blogspot.com/-PtTRd7A5BHk/Tw587Bu7HDI/AAAAAAAABDQ/qMyHZQnsjuE/s400/blog_sp_lock_1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;You can see that 56 was holding a &lt;b&gt;S (shared) lock&lt;/b&gt; on a &lt;b&gt;key&lt;/b&gt; resource (key = row lock on an index) of object 1349579846, which corresponds to the Buckets table.&lt;br /&gt;&lt;br /&gt;I wanted to the engine's execution plan for the reader "work queue" query.  To get this, I executed a query that I created a while ago to dump as many details about current sessions in the system as possible-- think of it as a "super who2":&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;select es.session_id, es.host_name, es.status as session_status, sr.blocking_session_id as req_blocked_by,&lt;br /&gt;datediff(ss, es.last_request_start_time, getdate()) as last_req_submit_secs,&lt;br /&gt;st.transaction_id as current_xaction_id, &lt;br /&gt;datediff(ss, dt.transaction_begin_time, getdate()) as xaction_start_secs,&lt;br /&gt;case dt.transaction_type &lt;br /&gt; when 1 then 'read_write' &lt;br /&gt; when 2 then 'read_only'&lt;br /&gt; when 3 then 'system'&lt;br /&gt; when 4 then 'distributed'&lt;br /&gt; else 'unknown'&lt;br /&gt;end as trx_type,&lt;br /&gt;sr.status as current_req_status, &lt;br /&gt;sr.wait_type as current_req_wait, &lt;br /&gt;sr.wait_time as current_req_wait_time, sr.last_wait_type as current_req_last_wait, &lt;br /&gt;sr.wait_resource as current_req_wait_rsc, &lt;br /&gt;es.cpu_time as session_cpu, es.reads as session_reads, es.writes as session_writes, &lt;br /&gt;es.logical_reads as session_logical_reads, es.memory_usage as session_mem_usage,&lt;br /&gt;es.last_request_start_time, es.last_request_end_time, es.transaction_isolation_level,&lt;br /&gt;sc.text as last_cnx_sql, sr.text as current_sql, sr.query_plan as current_plan&lt;br /&gt;from sys.dm_exec_sessions es&lt;br /&gt;left outer join sys.dm_tran_session_transactions st on es.session_id = st.session_id&lt;br /&gt;left outer join sys.dm_tran_active_transactions dt on st.transaction_id = dt.transaction_id&lt;br /&gt;left outer join &lt;br /&gt; (select srr.session_id, srr.start_time, srr.status, srr.blocking_session_id, &lt;br /&gt; srr.wait_type, srr.wait_time, srr.last_wait_type, srr.wait_resource, stt.text, qp.query_plan&lt;br /&gt; from sys.dm_exec_requests srr&lt;br /&gt; cross apply sys.dm_exec_sql_text(srr.sql_handle) as stt&lt;br /&gt; cross apply sys.dm_exec_query_plan(srr.plan_handle) as qp) as sr on es.session_id = sr.session_id&lt;br /&gt;&lt;br /&gt;left outer join &lt;br /&gt; (select scc.session_id, sct.text&lt;br /&gt; from sys.dm_exec_connections scc&lt;br /&gt; cross apply sys.dm_exec_sql_text(scc.most_recent_sql_handle) as sct) as sc on sc.session_id = es.session_id&lt;br /&gt;&lt;br /&gt;where &lt;br /&gt;es.session_id &amp;gt;= 50&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the above output, the last column is the SQL XML Execution plan.  Viewing that for spid 56, I confirmed my suspicion:  The plan to serve the "work queue" query was to seek the "music collection" index on the buckets table for 'Steves collection', then seek to the clustered index to confirm 'generation = 23', then seek into the bucketId index on the Cds table.  So to serve the WHERE clause in the "work queue" query, the engine had to use both the non-clustered index on Buckets and the clustered index (for the version predicate).&lt;br /&gt;&lt;br /&gt;When joining and reading rows at &lt;a href="http://msdn.microsoft.com/en-us/library/ms173763.aspx"&gt;READ COMMITTED isolation level&lt;/a&gt;, the engine will acquire locks as it traverses from index to index in order to ensure consistent reads.  Thus, to read the value of the generation in the Buckets table, it must acquire a shared lock.  And it has!&lt;br /&gt;&lt;br /&gt;The problem comes in when the competing sessions that are trying to update the size on that same record of the Bucket table.  It needs an &lt;b&gt;X (exclusive)&lt;/b&gt; lock on that row (highlighted in red), and eek! it can't get it, because that reader query has a conflicting S lock &lt;i&gt;already granted&lt;/i&gt; (highlighted in green).&lt;br /&gt;&lt;br /&gt;Ok so that all makes sense, but why is the S lock being held?  At READ COMMITTED you usually only hold the locks while the record is being read (there are exceptions and we'll get to that in Part 2).  They are released as soon as the value is read.  So if you read 10 rows in a single statement execution, the engine will: acquire lock on row 1, read row 1, release lock on row 1, acquire lock on row 2, read row 2, release lock on row 2, acquire lock on row 3, etc.  So none of the four workers are currently reading-- they are writing -- or at least they're trying to if that pesky reader connection wasn't blocking them.&lt;br /&gt;&lt;br /&gt;To find this, I was curious &lt;i&gt;why&lt;/i&gt; the reader query was in a SUSPENDED state (see original sp_who2 output above).  In the above "super who2" output, the &lt;b&gt;current_req_wait&lt;/b&gt; value for the "work queue" read query is &lt;code&gt;ASYNC_Network_IO&lt;/code&gt;.  &lt;br /&gt;&lt;h5&gt;ASYNC_Network_IO wait and how databases return results&lt;/h5&gt;ASYNC_Network_IO is an interesting wait.  Let's discuss how remote applications execute and consume SELECT queries from databases.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-cxEI-pucS6E/Tw6GNAiYOUI/AAAAAAAABDc/r5mOb8kcIdQ/s1600/db_arch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="207" src="http://1.bp.blogspot.com/-cxEI-pucS6E/Tw6GNAiYOUI/AAAAAAAABDc/r5mOb8kcIdQ/s400/db_arch.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;That diagram is over-simplified, but within the database there are two chunks of memory to discuss: the &lt;b&gt;buffer cache&lt;/b&gt; and the &lt;b&gt;connection network buffer&lt;/b&gt;.  The buffer cache is a shared chunk of memory, where the actual pages of the tables and indexes are kept to serve queries.  So parts of the Buckets and Cds tables will be in memory while this "work queue" query executes.  The execution engine executes the plan, it works out of the buffer cache, acquiring locks, and producing output records to send to the client.  As it prepares these output records, it puts them in a connection-specific &lt;b&gt;network buffer&lt;/b&gt;. &amp;nbsp;When the application reads records from the result set, its actually being served from the network buffer in the database.  The application driver typically has its own buffer as well.&lt;br /&gt;&lt;br /&gt;When you just execute a simple SQL SELECT query and don't explicitly declare a database cursor, MSSQL gives you what it calls the "default result set" -- which is still a cursor of sorts -- you can think of it as a cursor over a bunch of records that you can only iterate over once in the forward direction.  As your application threads iterate over the result set, the driver requests more chunks of rows from the database on your behalf, which in turn depletes the network buffer.  &lt;br /&gt;&lt;br /&gt;However, with very large result sets, the entire results cannot fit in the connection's network buffer.  If the application doesn't read them fast enough, then eventually the network buffer fills up, and the execution engine must stop producing new result records to send to the client application.  When this happens the spid must be suspended, and it is suspended with the wait event &lt;b&gt;ASYNC_Network_IO&lt;/b&gt;.  It's a slightly misleading wait name, because it makes you think there might be a network performance problem, but its more often an application design or performance problem. &amp;nbsp;Note that when the spid is suspended -- just like any other suspension -- the &lt;b&gt;currently held locks will remain held until the spid is resumed&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;In our case, we know that we have millions of CDs and we can't fit them all in application memory at one time.  We, by design, want to take advantage of the fact that we can stream results from the database and work on them in chunks.  Unfortunately, if we happen to be holding a conflicting lock (S lock on Bucket record) when the reader query is suspended, then we create a multi-layer application deadlock, as we observed, and the whole system screeches to a halt.&lt;br /&gt;&lt;br /&gt;So what to do for a solution?  I will discuss some options and our eventual decision in Parts 2 and 3.  Note that I gave one hint at our first attempt when I talked about "covering indexes", and then there is another hint above that we didn't get to in this post about "lock escalation".&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-3623214924238947736?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/3623214924238947736/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2012/01/multi-tier-application-database.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/3623214924238947736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/3623214924238947736'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2012/01/multi-tier-application-database.html' title='Multi-tier application + database deadlock &lt;i&gt;or&lt;/i&gt; why databases aren&apos;t queues (part1)'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-N3MdPdtexmg/Tw53LbINicI/AAAAAAAABDE/5-cfk-PB43o/s72-c/blog_sp_who2_1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-7552626102470802234</id><published>2011-12-22T12:17:00.002-06:00</published><updated>2011-12-22T12:27:58.631-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='indexes'/><category scheme='http://www.blogger.com/atom/ns#' term='deadlocks'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>MSSQL non-clustered indexes INCLUDE feature explained</title><content type='html'>Today I received a question from someone about the nature of the INCLUDE feature when creating a non-clustered (secondary) index on a table.  My response was a bit long, and I haven't posted in a while -- ergo blogpost!  Here's the question:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;What is your opinion about using the include statement when building your indexes?  I’ve never really used the included functionality and I’m curious if there are upsides or downsides to them.  My reading lead me to believe that I should use the include when I have a non clustered index and space appears to be a concern.   I can use the ‘include’ as part of the index with the columns that may not be used as frequently.  We’re using standard datatypes and nothing with very large column widths.  So is there a benefit to using include?&lt;/blockquote&gt;&lt;br /&gt;&lt;hl /&gt;&lt;br /&gt;&lt;br /&gt;A &lt;b&gt;clustered index&lt;/b&gt; is stored in a B+-tree data structure and the &lt;i&gt;actual data&lt;/i&gt; the whole row of data is in the leaf.  So if you think of a b-tree structure (simplified) as something like:&lt;br /&gt;&lt;pre&gt;           A&lt;br /&gt;         /   \&lt;br /&gt;        B     C&lt;br /&gt;       / \   / \&lt;br /&gt;      M   N O   P&lt;br /&gt;&lt;/pre&gt;And you have records like &lt;br /&gt;&lt;pre&gt;[ Id =1, FirstName = Steve, LastName = Ash ]&lt;br /&gt;[ Id = 2, FirstName = Neil, LastName=Gafter ]&lt;br /&gt;&lt;/pre&gt;Then for the &lt;i&gt;clustered&lt;/i&gt; index, the id data (id=1 and id=2) will exist at every node (A, B, C, M, N, O, P), but the bytes for “steve” “ash” “neil” “gafter” will only exist in leaf nodes (either M N O or P).  The id is used to &lt;i&gt;locate&lt;/i&gt; which leaf holds the whole record.  (note this is a simplification, see &lt;a href="http://en.wikipedia.org/wiki/B%2B_tree"&gt;Wikipedia&lt;/a&gt; for more info about b+-trees).  The noteworthy fact is being a &lt;i&gt;clustered&lt;/i&gt; index means that the whole record is in a leaf (i.e. M N O P).&lt;br /&gt;&lt;br /&gt;Now let’s think of a non-clustered index on lastName that corresponds to the clustered index above.&lt;br /&gt;&lt;pre&gt;           D&lt;br /&gt;         /   \&lt;br /&gt;        E     F&lt;br /&gt;       / \   &lt;br /&gt;      Q   R         &lt;br /&gt;&lt;/pre&gt;In this case the “last name” is used to &lt;i&gt;get to&lt;/i&gt; the leaf, and the leaf holds the &lt;i&gt;primary key&lt;/i&gt; of the corresponding row in the clustered index.  So D E or F will have things like lastName=Ash and lastName=Gafter and the leaves Q and R will have both lastnames and IDs.  So an entry in Q or R might look like [lastname=Ash, id=1] (again simplifying).  &lt;br /&gt;&lt;br /&gt;So if you issue a query like &lt;pre&gt;SELECT firstName FROM ThisTable WHERE lastName = ‘Ash’&lt;/pre&gt;(and the optimizer chooses to use the non-clustered index) then the database engine will do something like:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Seek the &lt;b&gt;non-clustered&lt;/b&gt; index for ‘Ash’&lt;/li&gt;&lt;ol&gt;&lt;li&gt;Look in D to decide which direction to go E or F (lets say E is the right choice)&lt;/li&gt;&lt;li&gt;Look in E to decide which direction to go Q or R (lets say Q is the right choice)&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Find the primary key for ‘Ash’&lt;/li&gt;&lt;ol&gt;&lt;li&gt;In Q find id for Ash – which is id=1&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Seek the &lt;b&gt;clustered index&lt;/b&gt; for id = 1&lt;/li&gt;&lt;ol&gt;&lt;li&gt;Look in A to decide which direction to go B or C (lets say B is right)&lt;/li&gt;&lt;li&gt;Look in B to decide which direction to go M or N (lets say N is right)&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Find the value of firstName for id=1&lt;/li&gt;&lt;ol&gt;&lt;li&gt;In N find firstName for id=1 which is ‘Steve’&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;return ‘Steve’ as the query result&lt;/li&gt;&lt;/ol&gt;Notice that we created a non-clustered indexed on lastName and we can use that index to quickly locate things by last name, but if we need any additional info, then we have to go &lt;i&gt;back&lt;/i&gt; to the clustered index to get the other info in the SELECT list.&lt;br /&gt;&lt;br /&gt;The “include” provides a way for you to shove additional information in the leaf nodes of non-clustered indexes (Q and R) to alleviate this "going back" to the clustered index.&lt;br /&gt;&lt;br /&gt;So had I created the non-clustered index above on LastName INCLUDE FirstName then the engine would only need to do:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Seek the &lt;b&gt;non-clustered&lt;/b&gt; index for ‘Ash’&lt;/li&gt;&lt;ol&gt;&lt;li&gt;Look in D to decide which direction to go E or F (lets say E is the right choice)&lt;/li&gt;&lt;li&gt;Look in E to decide which direction to go Q or R (lets say Q is the right choice)&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Find the firstName for ‘Ash’&lt;/li&gt;&lt;ol&gt;&lt;li&gt;In Q due to the include there is id=1 AND firstName=’Steve’ so we have the first name right here!&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;return Steve&lt;/li&gt;&lt;/ol&gt;So you get rid of that entire other seek into the clustered index.  This additional seek shows up as a “bookmark lookup” operation in the query plan in MSSQL 2008 &amp; MSSQL 2000 and just another join in MSSQL 2005 query plans.  Bookmark lookup is a join -- just with a different name to indicate its semantic role in the query plan.&lt;br /&gt;&lt;br /&gt;When you have an index such that they query can be completely served from the index without needing to go back to the clustered index – such an index is called a &lt;b&gt;covering index&lt;/b&gt;.  The index &lt;i&gt;covers&lt;/i&gt; the needs of the query completely.  And it’s a performance boost as you don’t need the other join.&lt;br /&gt;&lt;br /&gt;So this means a few things:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You can obviously only “include” fields in non-clustered indexes.  Clustered indexes already have all the fields in the leaf...so it doesn’t mean anything to include more.&lt;/li&gt;&lt;li&gt;You can only have one clustered-index for a table, but you can simulate have multiple clustered indexes by INCLUDing the rest of the columns on your secondary indexes&lt;/li&gt;&lt;li&gt;By duplicating the data in the secondary index, if you UPDATE firstName – you now have to update both the clustered index and the nonclustered index. (&lt;b&gt;Main trade-off consideration&lt;/b&gt;)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;This is also a &lt;i&gt;huge&lt;/i&gt; deadlock opportunity if you’re not using read committed snapshot isolation (RCSI) level.  Think of two queries: (1) &lt;pre&gt;UPDATE MyTable SET firstName = ‘Steve2’ where id = 1&lt;/pre&gt;and (2) &lt;pre&gt;SELECT shoeSize FROM MyTable WHERE lastName = ‘Ash’&lt;/pre&gt;(pretend shoe size is a new field that is in the clustered index but NOT in the non-clustered, i.e. NOT in the INCLUDE).  Then the SELECT will seek the non-clustered index, grab shared (S) locks, then (while holding S locks) traverse the clustered index.  Whereas (in the opposite order) the UPDATE will seek the clustered index, hold an exclusive (X) lock, then seek the non-clustered index to update firstName.  The fact that these two queries are holding incompatible locks in &lt;i&gt;opposite&lt;/i&gt; directions, is a deadlock waiting to happen.  If I had a dollar for every time I diagnosed this deadlock scenario...&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;By duplicating the data in the secondary index, each page in the leaf (Q and R) now has &lt;b&gt;fewer&lt;/b&gt; rows per page and thus there is a greater memory demand on the buffer cache (and more IO to get the same number of records) (&lt;b&gt;Second trade off consideration&lt;/b&gt;)&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Some databases (even MSSQL &lt; 2005) don’t support this feature, but you can approximate it by creating non-clustered indexes with compound keys.  I.e. if I created the non-clustered index on &lt;pre&gt;(lastName, firstName)&lt;/pre&gt;then the index still covers queries like &lt;pre&gt;SELECT firstName FROM myTable WHERE lastName = ‘Ash’&lt;/pre&gt;Note that this is not as good as the INCLUDE solution (for this particular query) as now the bytes of firstName=’Steve’ take up some space in non-leaf nodes D E F.&lt;br /&gt;&lt;/ol&gt;So deciding to use an INCLUDE is (like everything) a trade off.  If you have a performance critical query that is being executed frequently, then you can usually use INCLUDEs to reduce the number of joins and increase performance.  In an environment where CPU is more precious than memory this can be a big win (we can talk about cache locality benefits of includes later).  However, if you INCLUDE a column that will be UPDATEd later – then you often are shooting yourself in the foot as the cost can easily outweigh the benefit.&lt;br /&gt;&lt;br /&gt;Last tidbit about INCLUDEs that I’ll mention.  The sql index analyzer is REALLY aggressive about recommending you add indexes with lots of INCLUDEs.  This is because the index analyzer usually doesn’t know how many UPDATEs you’re doing.  Usually you just tell it what SELECT you want to speed up and it naively says “oh well of course if you add these three covering indexes, this SELECT will be faster.”  And while that’s true it doesn’t take into account the _total_ workload (UPDATEs DELETEs etc) so just be skeptical when you see this if you use the index analyzer.&lt;br /&gt;&lt;br /&gt;I can’t give you a hard and fast rule to say INCLUDE is GOOD or EVIL as – like everything with database performance tuning – it depends ;)&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-7552626102470802234?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/7552626102470802234/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/12/mssql-non-clustered-indexes-include.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/7552626102470802234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/7552626102470802234'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/12/mssql-non-clustered-indexes-include.html' title='MSSQL non-clustered indexes INCLUDE feature explained'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-2342218909150021588</id><published>2011-07-31T20:14:00.001-05:00</published><updated>2011-07-31T20:24:17.340-05:00</updated><title type='text'>Delegates or interfaces? Functional and OO Dualism</title><content type='html'>I have a mixed background: doing C#/.NET for ~4 years then switching to Java (switched jobs).  I have been in the Java enterprise ecosystem for the last 4 years.  I do mostly Java, but enjoy doing a little C# every now and again.  C# is really a nice language.  Shame its in such a horrible Microsoft-centric ecosystem.&lt;br /&gt;&lt;br /&gt;In any case, I've been writing a little thing in C# and needed a type with a single method to "doWork".  So coming from a Java bias I created a:&lt;br /&gt;&lt;pre&gt;public interface IWorker {&lt;br /&gt;   void DoWork();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Later, however, I wanted to offer the users an API option of just using a lambda to "doWork".  Unfortunately, there is no type conversion from a delegate to the matching "single method interface" (at least that I could find, if someone knows the answer, please share!). So as a shim, I created:&lt;br /&gt;&lt;pre&gt;public delegate void WorkerDelegate();&lt;br /&gt;&lt;br /&gt;public WorkerWrapper : IWorker {&lt;br /&gt;   readonly WorkerDelegate workerDelegate;&lt;br /&gt;   public WorkerWrapper(WorkerDelegate workerDelegate) {&lt;br /&gt;      this.workerDelegate = workerDelegate;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void DoWork() {&lt;br /&gt;      workerDelegate();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;So I wrap the lambda in a little wrapper and don't need to change my entire IWorker-based infrastructure.  It works, but I'm not happy with this.  I know that in the Java lambda mailing list, they are planning to include a "lambda conversion" so that lambdas can be converted to compatible single abstract method (SAM) types.  This would've alleviated my need for the shim above as I could've assigned the lambda directly to an IWorker and all would've been well.&lt;br /&gt;&lt;br /&gt;I believe the "C# way" would've been for me to use delegates all the way through to begin with.  Had someone come along with an IWorker interface then that would've been assignable to my WorkerDelegate.&lt;br /&gt;&lt;br /&gt;But is this the right answer?  Conceptually, how should I think of these?  What &lt;i&gt;is&lt;/i&gt; an IWorker in the above case?  Is it &lt;i&gt;really&lt;/i&gt; just a chunk of code that should be passed around as such?  Or is it a member in the space of collaborating types that make up my system...&lt;br /&gt;&lt;br /&gt;This is an example of the conceptual problems reconciling a Functional view of the world with an object oriented view of the world (dualism).  I know that many people smarter than me have thought about these problems, and I'm hoping that I can find some good articles discussing them.  &lt;br /&gt;&lt;br /&gt;It &lt;i&gt;feels&lt;/i&gt; like we're describing the same concept: a "chunk of code" that is defined by an interface (call it a delegate or a SAM, same thing).  I don't think that I would have any dissonance if both were assignable to each other, and thus can be treated as different expressions of the same concept.  If this were the case, then maybe I would view delegates as just SAM types -- so my "world view" is still object oriented, I just have an additional, concise lambda syntax to create SAMs.  Actually, if this were the case, then you could probably invoke the Liskov substitution principle and call functional-OO dualism reconciled... &lt;br /&gt;&lt;br /&gt;But something still seems amiss.  There is more to the identity of an IWorker than the fact that it takes no arguments and returns nothing.  I suppose the same questions are true of reconciling structural typing to static typing.  Hmm.. I have a lot of reading to do.&lt;br /&gt;&lt;br /&gt;I imagine there are a number of philosophical problems between functional and OO.  This is just the one I ran across and felt dissonance with C#s implementation.  Maybe they truly are different things and should be treated as such.  I hope (despite my extremely small readership) to get some links to articles on this topic.&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-2342218909150021588?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/2342218909150021588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/07/delegates-or-interfaces-functional-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/2342218909150021588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/2342218909150021588'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/07/delegates-or-interfaces-functional-and.html' title='Delegates or interfaces? Functional and OO Dualism'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-3540756829061030092</id><published>2011-07-09T13:59:00.001-05:00</published><updated>2011-07-09T14:37:59.405-05:00</updated><title type='text'>I &lt;3 Robert C. Martin</title><content type='html'>&lt;div&gt;Im reading Clean Code by Uncle Bob. I have read sections of this book before when it came out and actually had the pleasure of watching Robert Martin present it at SDWest in 2008. I've decided to read the whole thing.&lt;br/&gt; &lt;br/&gt; A while ago, I read a blog post from someone who was arguing that software wasn't a craft but a trade. I believe the authors intention was to say that we software developers should recognize that the value of the software is the business value and thus we shouldn't wax philosophic about "elegance in design" or software aesthetics as that was all wasting time trying to get to the goal. I may be misrepresenting the author's intention. I couldn't find the post to link it.&lt;br/&gt; &lt;br/&gt; In any case, I disagreed entirely with this opinion. While I agree that business value is the motivator-- the craft aspects such as aesthetics, conceptual purity, elegance, etc. All contribute to the solution and its extensibility and maintainability. Maybe we're just arguing over the definition of craft, trade, or art, but in any case I feel there is value in &lt;/i&gt;recognizing the challenge&lt;/i&gt; of good engineering for today and tomorrow.   The masters do it almost effortlessly-- almost accidentally.  That &lt;i&gt;feels&lt;/i&gt; like art to me and thus should be labelled appropriately as craft.&lt;br/&gt; &lt;br/&gt; To this point, clean code is more art than science and Mr. Martin has something to say about it that I really enjoyed:&lt;blockquote&gt;Every system is built from a domain specific language designed by the programmers to describe their system.  Functions are the verbs, classes are the nouns.  This is not some throwback to the hideous old notion that the nouns and verbs in a requirements document are the first guess of the classes and functions of a system.  Rather, this is a much older truth. The art of programming is, and always has been,  the art of language design.&lt;br/&gt; &lt;br/&gt; Master programmers think of systems as stories to be told rather than programs to be written.  They use facilities of their chosen programming language to construct a much richer and more expressive language that can be used to tell that story.  Part of that domain-specific language is the hierarchy of functions that describe all the actions that take place within that system.  In an artful act of recursion those actions are written to use the very domain specific language they define to tell their own small part of the story.&lt;/blockquote&gt;So to argue that software is not art is to &lt;i&gt;naively&lt;/i&gt; ignore the reality that language is hard and has a dramatic effect on the bottom line of your code base. How many software systems never change or never need to be understood after they are written? Such systems must not be very interesting or do anything important.&lt;br/&gt; &lt;br/&gt; Let's recognize the art of good software engineering!  It will motivate us to continue to improve if we recognize these things have a value.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-3540756829061030092?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/3540756829061030092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/07/i-robert-c-martin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/3540756829061030092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/3540756829061030092'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/07/i-robert-c-martin.html' title='I &amp;lt;3 Robert C. Martin'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-7398747922265109516</id><published>2011-06-27T22:54:00.001-05:00</published><updated>2011-12-22T12:33:01.607-06:00</updated><title type='text'>Maven -_-</title><content type='html'>I have spent the last few days mucking about with POM files.  Anyone that has done this understands where I'm going with this.  So I'll just leave these two quotes that fit nicely:&lt;br /&gt;&lt;blockquote&gt;But there has to be something fundamentally wrong with any tool that, whenever I use it, seems to have at least a 50% chance of completely fucking up my day.&lt;br /&gt;&lt;br /&gt;-&lt;a href="http://fishbowl.pastiche.org/2007/12/20/maven_broken_by_design/"&gt;Charles Miller&lt;/a&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;blockquote&gt;The people who &lt;b&gt;love&lt;/b&gt; Maven &lt;b&gt;love the theory&lt;/b&gt;. The people who &lt;b&gt;hate&lt;/b&gt; Maven &lt;b&gt;hate the reality&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;-&lt;a href="http://www.alittlemadness.com/2007/08/01/the-problem-with-maven/"&gt;Zutubi&lt;/a&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Frustration.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;EDIT:&lt;/b&gt; For anyone running across this.  Now that I am well over the "learning curve" hump.  I &amp;lt;3 maven.  Seriously.  Yes there are some rough edges-- I really should be able to delete folders simply without having to do ant-runs, etc.  But its benefit drastically outweighs its cost in our environment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-7398747922265109516?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/7398747922265109516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/06/maven.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/7398747922265109516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/7398747922265109516'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/06/maven.html' title='Maven -_-'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-5636683094426768315</id><published>2011-04-13T10:18:00.002-05:00</published><updated>2011-04-13T18:56:27.733-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='reading list'/><title type='text'>OO Reading List</title><content type='html'>One of my favorite parts of &lt;a href="http://www.amazon.com/Pragmatic-Thinking-Learning-Refactor-Programmers/dp/1934356050/ref=sr_1_1?ie=UTF8&amp;qid=1302699408&amp;sr=8-1"&gt;Pragmatic Thinking&lt;/a&gt; is the description of the Dreyfuss model of skills acquisition.  This describes the phenomena of how people are frequently distributed along some non-trivial "skill" (like programming), and defines metrics about what differentiates each skill level.  Overall, as one moves up to higher skill levels, they are increasing their &lt;i&gt;intuition&lt;/i&gt; about the problem space.  Intuition is something for which we must &lt;i&gt;train&lt;/i&gt; our brain through experience and knowledge.  To that end, there are a few books that have helped me in increasing my &lt;i&gt;intuition&lt;/i&gt;, which I would like to catalog.  If you have others that are missing, please leave a comment!  My appetite for the amazon marketplace is insatiable ;-)&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;OO and Design Patterns&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod"&gt;But Uncle Bob Essay on SOLID&lt;/a&gt; - This is Robert C Martin's article outlining the principles of Object Oriented Design.  If SOLID doesn't ring a bell, then start with this article.  Note that the &lt;a href="http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29"&gt;now-famous acronym: SOLID&lt;/a&gt; is not actually mentioned in this post, but Robert C Martin is still credited as the inventor.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445/ref=sr_1_1?ie=UTF8&amp;qid=1302708855&amp;sr=8-1"&gt;Agile Software Development Principles, Patterns, and Practices&lt;/a&gt; - lovingly referred to as the PPP book (not to be confused with the protocol).  This describes much of the &lt;i&gt;why&lt;/i&gt; of OOD, and explains the Agile mindset.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0201379430/sr=1-1/qid=1302709079/ref=olp_product_details?ie=UTF8&amp;me=&amp;qid=1302709079&amp;sr=1-1&amp;seller="&gt;Object Design&lt;/a&gt; - another often referenced book describing the &lt;i&gt;why&lt;/i&gt; and various design decisions that go into object oriented design.  Thinking about objects isn't hard-- category theory and abstraction is something that our brain does quite naturally (hence the appeal of this design methodology).  However, the ergonomics of OOD can sometimes lead to an undeserved sense of self-confidence in one's design.  It may &lt;i&gt;feel&lt;/i&gt; like OOD, because "oh look-- there are objects there! inheritance! oh, my!", but within the context of software engineering there are many factors that make some designs good and some bad.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302705445&amp;sr=1-1"&gt;Design Patterns: Elements of Reusable Object Oriented Software&lt;/a&gt; - Canonical book by the "gang of four".  Has good description of patterns and &lt;i&gt;why&lt;/i&gt; they are useful.  If you prefer something with prettier pictures, starting with the &lt;a href="http://www.amazon.com/First-Design-Patterns-Elisabeth-Freeman/dp/0596007124/ref=sr_1_2?s=books&amp;ie=UTF8&amp;qid=1302705445&amp;sr=1-2"&gt;Head First Design Patterns&lt;/a&gt; book is nice too&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Essays-Object-Oriented-Software-Engineering-Oriented/dp/0132888955/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302705325&amp;sr=1-1"&gt;Essays on OO Software Engineering&lt;/a&gt; - Maybe not terribly well-known, but well written theoretical overview of OO, the design forces in OOD, and the motivations behind them.  I had the pleasure to work with Ed.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302705155&amp;sr=1-1"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; - Canonical book on patterns by the man himself&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302705613&amp;sr=1-1"&gt;Refactoring&lt;/a&gt; - Another Fowler book.  I haven't read this one cover to cover, but read many chapters.  Good examples to get your head around particular refactoring patterns&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302705711&amp;sr=1-1"&gt;Domain Driven Design&lt;/a&gt; - Eric Evan's now-famous book on how to turn business problems into rich object models.  Great read.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Real-World-Patterns-Rethinking-Practices/dp/0557078326/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302706432&amp;sr=1-1"&gt;Real World Java EE Patterns&lt;/a&gt; - while this is a "java" book, the principles and design trade-off analysis that Adam Bien does for each pattern is universal.  Good read.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Code&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302705967&amp;sr=1-1"&gt;Beautiful Code&lt;/a&gt; - Essays where programmers reflect on what is beauty in code.  As the authors wax philosophic about their "beautiful" examples, you glean insight into their thought process.  It's a nice read.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Implementation-Patterns-Kent-Beck/dp/0321413091/ref=sr_1_1?ie=UTF8&amp;qid=1302706192&amp;sr=1-1-spell"&gt;Implementation Patterns&lt;/a&gt; - Kent Beck's book about the low-level decisions we make as we actually type the code and create APIs (knowingly or unknowingly).  One of my &lt;b&gt;top recommendations&lt;/b&gt;.  I really love this book.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302706270&amp;sr=1-1"&gt;Clean Code&lt;/a&gt; - guide to code readability, writing code at appropriate abstractions, etc. A nice companion to Implementation Patterns.  I had the pleasure of meeting Robert C Martin at a conference once. I'm sure I acted appropriately star-struck.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Other "meta" and philosophic musings&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Notes-Synthesis-Form-Harvard-Paperbacks/dp/0674627512/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1302706811&amp;sr=1-1"&gt;Notes on the Synthesis of Form&lt;/a&gt; - this is not a computer science book, but deals with the theory of what is design, and describes a methodology for decomposition.  It's a quick read, and at least the first half is worth the exploration just to get some ideas in your head about methodologies for design.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302706921&amp;sr=1-1"&gt;Pragmatic Programmer&lt;/a&gt; - what list would be complete without this one! Good overview of the pragmatic aspects of becoming a good programmer.  From problem solving skills to tools, this is required reading.  I think if you read this, then skip or at most skim &lt;a href="http://www.amazon.com/Productive-Programmer-Theory-Practice-OReilly/dp/0596519788/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302707582&amp;sr=1-1"&gt;Productive Programmer&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Pragmatic-Thinking-Learning-Refactor-Programmers/dp/1934356050/ref=sr_1_3?s=books&amp;ie=UTF8&amp;qid=1302706921&amp;sr=1-3"&gt;Pragmatic Thinking: Refactoring your wetware&lt;/a&gt; - I mentioned this at the front of the post.  I enjoyed most of this book-- in particular the front half.  Some of the topics towards the end were less interesting, because they were more familiar.  In any case at least the first three chapters are a great read.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Little-Schemer-Daniel-P-Friedman/dp/0262560992/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302707349&amp;sr=1-1"&gt;The Little Schemer&lt;/a&gt; - you can run through this in an afternoon.  This is an introduction to recursion, via Scheme/LISP.  This book is fun, because its a simple and unique format, but takes you through a journey that helps shape your mind to "think" of solutions recursively.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Beautiful-Architecture-Leading-Thinkers-Software/dp/059651798X/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302707446&amp;sr=1-1"&gt;Beautiful Architecture&lt;/a&gt; - This one wasn't as successful to me as Beautiful Code, but still worth a read.  There are a few chapters you can skip, but a few (especially the first chapter) that are great.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Beautiful-Data-Stories-Elegant-Solutions/dp/0596157118/ref=sr_1_2?s=books&amp;ie=UTF8&amp;qid=1302707446&amp;sr=1-2"&gt;Beautiful Data&lt;/a&gt; - I'm only about half way through this one, and need to spend more time with it, as I'm sure there are some gems in there I have yet to run across.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Reading TO-DOs&lt;/h2&gt;These are books that are on my shelf to read next (that are relevant to this list at least)&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Design-Essays-Computer-Scientist/dp/0201362988/ref=sr_1_1?s=books&amp;ie=UTF8&amp;qid=1302706584&amp;sr=1-1"&gt;The Design of Design&lt;/a&gt; - Fred Brooks (of Mythical Man Month fame) waxes about design philosophies and goes through a few case studies.  Im really excited to make time for this.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Masterminds-Programming-Conversations-Creators-Languages/dp/0596515170/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1302707225&amp;sr=1-1"&gt;Masterminds of Programming&lt;/a&gt; - interviews with the creators of many of the major languages used over the last 30 years.  Looks promising.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Thoughtworks-Anthology-Technology-Innovation-Programmers/dp/193435614X/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1302707626&amp;sr=1-1"&gt;Thoughtworks Anthology&lt;/a&gt; - I am going to be selective in which essays I read. I will start with those that are most applicable to my world, and then move out to the more "exotic" (relatively).  I know that there are essays in here that will be more useful to my current world than others.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I feel like I'm missing some...&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-5636683094426768315?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/5636683094426768315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/04/oo-reading-list.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/5636683094426768315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/5636683094426768315'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/04/oo-reading-list.html' title='OO Reading List'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-5257950004997875539</id><published>2011-03-06T14:15:00.003-06:00</published><updated>2011-04-13T10:19:51.103-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='reading list'/><title type='text'>Spring 2011 Reading List</title><content type='html'>I gave last Spring's reading list (which was more what I had read in the previous year) in &lt;a href="http://manycupsofcoffee.blogspot.com/2010/01/spring-reading-list.html"&gt;this post&lt;/a&gt;.  This spring, I am going to write what I am currently reading or hope to finish this Spring.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/193435659X"&gt;Seven Languages in Seven Weeks&lt;/a&gt; - this book looks interesting, and certainly the challenge of trying to &lt;i&gt;meaningfully&lt;/i&gt; cover seven languages and language philosophies in a short book will be interesting if nothing else.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0981531601"&gt;Programming in Scala&lt;/a&gt; - I have "played" with Scala, but not done more than toy programs.  However, from everything I know about it- I believe I will really enjoy it.  I buy into a lot of the functional vs object oriented dualism, and certainly enjoy the terse syntax.  I think there are still interesting questions with regards to software development economics (i.e. how do we integrate Scala in a team of mixed skill levels, cost trade offs, etc.)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0976458705"&gt;Thinking Forth&lt;/a&gt; - I'm not particularly interested in Forth, but I am interested in language design and problem solving philosophies.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0321125215"&gt;Domain Driven Design&lt;/a&gt; - this was recommended by a commenter in the previous post, and I picked it up.  I've made it through quite a bit of the book, and have enjoyed it so far.  I had always heard this book referenced by Fowler et al, and really I should've read it years ago...&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/3540288457"&gt;Introduction to Reliable Distributed Programming&lt;/a&gt; - its a Springer book, 'nuff said.  I've been through a few distributed computing books, and have a fairly broad knowledge of the space.  I am hoping to get a more mature, theoretically rigorous view of the problems now.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0123705916 "&gt;The Art of Multiprocessor Programming&lt;/a&gt; - this book is great.  I enjoyed the nice mix of intuitive description and theoretical rigor.  It's a nice blend of theory and pragmatics.  I really recommend reading this for anyone that wants in depth knowledge of parallel computing.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/1604390085"&gt;Introductions to Neural Networks for Java&lt;/a&gt; - this was an interesting book to &lt;i&gt;skim&lt;/i&gt; I don't really want to recommend it, because most of it is explaining his neural network code base instead of the underlying concepts.  In any case, its a nice thing to skim over an afternoon.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0131471392 "&gt;Neural Networks and Learning Machines&lt;/a&gt; - Great textbook for all the theoretical background and mathematical proofs regarding Neural Nets and machine learning.  I've had to crack open my calc books to freshen up while going through this...its dense.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0121942759"&gt;Fuzzy Models and Genetic Algorithms for Data Mining and Exploration&lt;/a&gt; - I can't recommend this book (see my Amazon review if you're curious why), but it does provide a decent vocab overview for the topics it covers.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Well that's it for the moment! This spring is a bit more theory heavy than the previous list... that probably reflects my work and school change, which has been more research oriented in the last year.  I'm not moving away from the &lt;i&gt;real world&lt;/i&gt; just trying to get a more comprehensive grasp on both worlds in the areas which I am interested.  I've always thought this was one of my strengths-- knowing enough theory to shape my thinking skills and be knowledgeable-- but continuously, deliberately enriching my implementation and practical skills to actually put the theory to good use!  The intersection of the two is the more rewarding area for me, I think.&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-5257950004997875539?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/5257950004997875539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/03/spring-2011-reading-list.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/5257950004997875539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/5257950004997875539'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/03/spring-2011-reading-list.html' title='Spring 2011 Reading List'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-8732917677609773565</id><published>2011-03-05T13:46:00.003-06:00</published><updated>2011-03-05T14:05:07.132-06:00</updated><title type='text'>Building Derby with Eclipse gotcha</title><content type='html'>This is partially a reference for myself and for anyone else trying to build Derby 10 from source using Eclipse.  My thesis project involves modifying the on-disk page layout for a database (long story for another post).  I am currently using Eclipse for my IDE.  I just wanted to document a quick pain point in case anyone else is Googling for the answer:&lt;br /&gt;&lt;br /&gt;I wanted to do a &lt;b&gt;clean build&lt;/b&gt; of all of Derby.  So I set up an ANT External Tool launch configuration:&lt;ul&gt;&lt;li&gt;Launch the build.xml from the root Derby directory&lt;/li&gt;&lt;li&gt;Working directory is the root Derby directory&lt;/li&gt;&lt;li&gt;Refresh resources upon completion&lt;/li&gt;&lt;li&gt;Uncheck the "build before launch&lt;/li&gt;&lt;li&gt;Choose the &lt;b&gt;clobber, all&lt;/b&gt; targets (clobber is a total clean)&lt;/li&gt;&lt;li&gt;Defaults for everything else&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I started to build and failed with the follow errors (I'm omitting some of the ones in the middle):&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;    [javac] C:\DerbyDev\java\build\org\apache\derbyBuild\javadoc\DiskLayoutTaglet.java:24: package com.sun.tools.doclets does not exist&lt;br /&gt;    [javac] import com.sun.tools.doclets.Taglet;&lt;br /&gt;    [javac]                             ^&lt;br /&gt;    [javac] C:\DerbyDev\java\build\org\apache\derbyBuild\javadoc\DiskLayoutTaglet.java:25: package com.sun.javadoc does not exist&lt;br /&gt;    [javac] import com.sun.javadoc.*;&lt;br /&gt;    [javac] ^&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;    [javac] C:\DerbyDev\java\build\org\apache\derbyBuild\javadoc\UpgradeTaglet.java:102: cannot find symbol&lt;br /&gt;    [javac] symbol  : class Taglet&lt;br /&gt;    [javac] location: class org.apache.derbyBuild.javadoc.UpgradeTaglet&lt;br /&gt;    [javac]        Taglet t = (Taglet) tagletMap.get(tag.getName());&lt;br /&gt;    [javac]                    ^&lt;br /&gt;    [javac] 40 errors&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;So as you can see from the first two errors -- its missing the dependency for Javadoc things.  As it turns out Derby defines its own Taglet for Javadoc processing.  All of these classes are defined in the JDKs tools.jar file, which is in the JDK's/lib directory (not included in the JRE).&lt;br /&gt;&lt;br /&gt;Derby's build script includes tools.jar by: &lt;br /&gt;&amp;lt;pathelement path="${java15compile.classpath};${java.home}/../lib/tools.jar"/&amp;gt;&lt;br /&gt;&lt;br /&gt;Well I went to my command prompt and looked at my environment variable and java_home was set to the JDKs location.  So this should've worked.  I added a new launch configuration to execute the &lt;b&gt;showenv&lt;/b&gt; target, and added an echo statement to include java.home.&lt;br /&gt;&lt;br /&gt;I ran this and low and behold it was pointing to the JRE path, which doesn't have a tools.jar.  So the world made sense now.  In my eclipse installation, I have both the JDK and JRE installations defined in my preferences.  For some reason (subconscious desire to shoot myself in the foot?), the JRE was chosen for this workspace, and thus the launch configuration used that.&lt;br /&gt;&lt;br /&gt;So to resolve the problem, you can change the external tools launch configuration for the ANT script.  Go to the JRE tab and select &lt;b&gt;separate JRE&lt;/b&gt; and choose the JDK.  Or change the default JRE for the workspace, and leave the launch configuration set to &lt;b&gt;run in the same JRE as the workspace&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;This isn't a tricky problem, but can be a little misleading...plus I haven't posted in a while ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-8732917677609773565?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/8732917677609773565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/03/building-derby-with-eclipse-gotcha.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/8732917677609773565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/8732917677609773565'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2011/03/building-derby-with-eclipse-gotcha.html' title='Building Derby with Eclipse gotcha'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-6100214931029064560</id><published>2010-12-01T15:55:00.005-06:00</published><updated>2010-12-01T18:04:27.936-06:00</updated><title type='text'>Defender Methods: Neal Gafter is *not* painting a bike shed</title><content type='html'>I follow the lambda-dev mailing list, and there is a discussion going on regarding &lt;b&gt;defender methods&lt;/b&gt; and &lt;b&gt;automatic disambiguation&lt;/b&gt;. Defender methods are a language feature that allows some interesting things: &lt;ol&gt;&lt;li&gt;Adding methods to existing interfaces &lt;i&gt;without&lt;/i&gt; breaking backwards compatability&lt;/li&gt;&lt;li&gt;Multiple inheritance of behavior (but not state-- think interfaces with code)&lt;/li&gt;&lt;/ol&gt;The second item is profound (well both are), and it will be interesting to see how its usage is adopted by the community.  The approach taken with defender methods is described as "declaration-site" extension methods, because it has a syntax present in the interface declaration.  For example, imagine adding a "randomElement" method to java.util.Collection.  Today, you can't do that without breaking every class that implements Collection.  Using a defender method you can do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;interface Collection&amp;lt;T&amp;gt; {&lt;br /&gt;   T randomElement() default Collections.randomElement;&lt;br /&gt;   ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// add the default implementation to Collections&lt;br /&gt;public class Collections {&lt;br /&gt;   ...&lt;br /&gt;   public static &amp;lt;T&amp;gt; randomElement(Collection&amp;lt;T&amp;gt; c) {&lt;br /&gt;      ...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Other languages implement use-site extension methods (e.g. C#) in which case the owner of Collections doesn't have change anything.  You could write (in C#)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;namespace SteveCode.Extensions {&lt;br /&gt;   static class MyCollections {&lt;br /&gt;      T randomElement(this ICollection&amp;lt;T&amp;gt; c) {&lt;br /&gt;         ...&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}  &lt;br /&gt;&lt;/pre&gt;Then anywhere you state "using SteveCode.Extensions" (i.e. "import" the namespace) then you can invoke someCollection.randomElement() and the compiler will invoke the static method.  Ergo, the "user" of the extension method has to indicate that he is using it.&lt;br /&gt;&lt;br /&gt;There are some great articles out there discussing the pros/cons of declaration vs use site approaches, so I wont re-hash them.  See &lt;a href="http://weblogs.java.net/blog/2007/11/29/java-7-extension-methods"&gt;Remi Forax's post&lt;/a&gt; and &lt;a href="http://weblogs.java.net/blog/forax/archive/2009/11/28/why-extension-methods-are-evil"&gt;another&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Java is going with declaration-site style and one of the benefits as now inheritance can still work.  I.e. if you augment an interface to add a new method with a "default" and then later in your concrete class you wish to specialize this new method, you can override it and consumers will call the specialized method via polymorphism (as expected).  This doesn't work with use-site extension methods as they are just a compiler trick.&lt;br /&gt;&lt;br /&gt;So on the lambda-dev list, there has been a discussion over a particular feature in the current specification.  Take the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Impls {&lt;br /&gt;   public static void one(Object o) { ... }&lt;br /&gt;   public static void two(A a) { ... }&lt;br /&gt;   public static void two(B b) { ... }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;interface A {&lt;br /&gt;   void execute() default Impls.one;&lt;br /&gt;}&lt;br /&gt;interface B {&lt;br /&gt;   void execute() default Impls.one;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;If you defined a class MyAB implements A,B what would you expect to happen? Well currently the spec allows this "automatic disambiguation" because they both delegate to the same method/overload.  Where this becomes a problem is if later the owner of B &lt;i&gt;changes&lt;/i&gt; the default of B to something else.  Now when you recompile MyAB suddenly it breaks and you must manually disambiguate by implementing execute inside MyAB.&lt;br /&gt;&lt;br /&gt;Note that if the interfaces took a different overload of the default, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;interface A {&lt;br /&gt;   void execute() default Impls.two;&lt;br /&gt;}&lt;br /&gt;interface B {&lt;br /&gt;   void execute() default Impls.two;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now class MyAB implements A,B would fail right off the bat the first time-- because there is no overload in common.  Thus, automatic disambiguation would not work in this case either (nor would you want it).  Lastly, imagine having:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public class Impls {&lt;br /&gt;   public static void one(Object o) { ... }&lt;br /&gt;   public static void two(Object o) { ... }&lt;br /&gt;   public static void two(A a) { ... }&lt;br /&gt;   public static void two(B b) { ... }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;In this case, I &lt;i&gt;think&lt;/i&gt; (and someone please correct me if I'm wrong) that normal overload rules will apply and this will compile, invoking two(Object o) in MyAB, but in another class that only extends A, it would call two(A a).&lt;br /&gt;&lt;br /&gt;There is also some additional complexity that must be added somewhere (talk of changing the .class file format to include linking information) so that in the presence of changing versions of the interface, the code compiled against the old version will still work (remain binary compatible).  &lt;br /&gt;&lt;br /&gt;All in all, there is some complexity for doing this "automatic disambiguation".  Neal Gafter of Java Puzzlers, Sun Java spec, etc. fame (not in that order) has raised the point of whether this complexity is worth it.  One of his proposed solutions is just to use method bodies in the interface itself.  This would remove the overload resolution of the "default" entirely, moving the disambiguation to the client at the very beginning instead of making it potentially a source incompatible change to modify the defender's default (probably unexpected).  This seems like an entirely reasonable conversation to have on the mailing list as its a language detail that has the potential to have expensive impacts.  The discussion was going on for a few days-- and Reinier Zwitserloot (of Project Lambok, etc. fame) chimes in with:&lt;br /&gt;&lt;blockquote&gt; &lt;br /&gt;When is the currently proposed "default method is part of the signature" aspect going to result in a problem? If this is about ideological purity, then I vote we stop painting this bikeshed.&lt;/blockquote&gt; &lt;br /&gt;If you don't get the bikeshed reference, check &lt;a href="http://en.wikipedia.org/wiki/Parkinson%27s_Law_of_Triviality"&gt;here&lt;/a&gt;.  In classic Neal fashion, he had a precise (ableit somewhat arrogant) retort, which upon reading I had a good chuckle:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;An interesting reference to Parkinson's *law of triviality*.  The basis of Parkinson's law is that people seem to care less about issues they don't understand, no matter how important those issues might be relative to the issues for which they express a preference.  "painting this bikeshed" is an *ad hominem* suggestion that the participants in this discussion are really only commenting on those issues simple enough for them to understand.&lt;br /&gt;&lt;br /&gt;In this case, however, you also appear to be saying that you don't fully appreciate the issue, and therefore don't see any point in continuing the discussion.  Which ironically reinforces the reference to Parkinson's law.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;I like Neal Gafter and Reinier Zwitserloot and am happy that everyone on the dev lists cares enough to take time to discuss these things for the (I believe) betterment of the Java community.  Its not to say there should be unbounded discussion, but I personally found Neal's points useful.  Automatic disambiguation does &lt;i&gt;seem&lt;/i&gt; to require complexity that outweighs its benefit-- and that's worth discussing.&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-6100214931029064560?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/6100214931029064560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/12/defender-methods-neal-gafter-is-not.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/6100214931029064560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/6100214931029064560'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/12/defender-methods-neal-gafter-is-not.html' title='Defender Methods: Neal Gafter is *not* painting a bike shed'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-8344455637981293467</id><published>2010-11-19T17:18:00.002-06:00</published><updated>2010-11-19T17:24:49.900-06:00</updated><title type='text'>Notes on the Synthesis of Form</title><content type='html'>I just got in Notes on the Synthesis of Form by Christopher Alexander.  This is an academic, theoretical treatise on design.  It deals with "what is design", etc.  It was written in 1962, but the ideas are obviously still relevant today--even though some of the motivating examples seem a little anachronistic.  While it has nothing to do with software architecture (the term didn't even exist back then!), it is a fascinating read for those of us who enjoy some philosophical pandering every once in a while.&lt;br /&gt;&lt;br /&gt;In the introduction "The Need for Rationality", the author makes clear his opinion of self-proclaimed "artists":&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;The modern designer relies more and more on his position as an "artist", on catchwords, personal idiom and intuition--for all of these relieve him of some of the burden of decision, and make his cognitive problems manageable.  Driven on his own resources, unable to cope with the complicated information he is supposed to organize, he hides his incompetence in a frenzy of artistic individuality.  As his capacity to invent clearly conceived, well-fitting forms is exhausted further, the emphasis on intuition and individuality only grows wilder.  &lt;br /&gt;&lt;br /&gt;In this atmosphere, the designer's greatest gift, his intuitive ability to organize physical form, is being reduced to nothing by the size of the tasks in front of him, and mocked by the efforts of "artists".  What is worse, in an era that badly needs designers with the synthetic grasp of organization of the physical world, the real work has to be done by less gifted engineers, because the designers hide their gift in irresponsible pretension to genius.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;(pg. 11)&lt;br /&gt;&lt;br /&gt;I always enjoy well-written calls to humility.  I wonder who in particular the author had in mind when he wrote this?  What's funny is this was written in 1962 and seems no less valid in 2010 :-)&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-8344455637981293467?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/8344455637981293467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/11/notes-on-synthesis-of-form.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/8344455637981293467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/8344455637981293467'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/11/notes-on-synthesis-of-form.html' title='Notes on the Synthesis of Form'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-5354496707233181473</id><published>2010-10-26T10:23:00.005-05:00</published><updated>2010-10-26T13:27:00.567-05:00</updated><title type='text'>Deadlock two ways (Microsoft SQL Server)</title><content type='html'>&lt;span style="font-style:italic;"&gt;Think of "Duck two-ways" or some other delicious meal. This is as tasty ;-)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If you have done any development with Microsoft SQL Server with even a moderate amount of concurrent processing, then you have probably witnessed the dreaded:&lt;blockquote&gt;Transaction (Process ID 42) was deadlocked on lock resources with another process and has been chosen as the deadlock victim&lt;/blockquote&gt;This indicates that Microsoft SQL Server has detected a deadlock and has killed one of the processes to eliminate the stale mate.  Let's go through two very different deadlock conditions that have the same central theme.  I have found these to be &lt;i&gt;extremely&lt;/i&gt; common in production.  &lt;br /&gt;&lt;br /&gt;Intuitively deadlock is a "deadly embrace" that occurs when two resources are being contended over.  Thus, people are often surprised when they get deadlocks and "they weren't touching the same records!".  Unfortunately, in traditional pessimistic locking solutions to concurrency control, other records must be locked to guarantee ACID properties.  There are other solutions to concurrency control (MVCC is the one everyone reaches for) that avoid most/all of this.  However, MSSQL by default employs a pessimistic approach.  Also, I don't mean to sound as if I'm picking on MSSQL-- the principles below are true in a number of database implementations.  DB/2 is also particularly nasty with pessimistic locking style deadlocks.  The two presented below were reported to the JTDS forums, and are from real production.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Deadlock I&lt;/h2&gt;&lt;br /&gt;&lt;i&gt;This is the common unicode "mismatch" problem.  This has been covered on blogs a lot. I am going to use this as a way to describe how to research deadlocks.  But if you're already bored, go ahead and skip to &lt;b&gt;Deadlock II&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;Here is a table definition (slightly changed for brevity):&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;  CREATE TABLE [PM1](&lt;br /&gt;  [ID] [bigint] IDENTITY(1,1) NOT NULL,&lt;br /&gt;  [KEYNAME] [varchar](50) NOT NULL,&lt;br /&gt;  [VALUE] [varchar](255) NULL,&lt;br /&gt;  CONSTRAINT [PK_PM1] PRIMARY KEY CLUSTERED&lt;br /&gt;  (&lt;br /&gt;  [ID] ASC&lt;br /&gt;  ))&lt;br /&gt;  CREATE NONCLUSTERED INDEX [pmkv_idx] ON [PM1]&lt;br /&gt;  (&lt;br /&gt;  [KEYNAME] ASC,&lt;br /&gt;  )&lt;br /&gt;&lt;/blockquote&gt;Now we execute multiple, concurrent:&lt;br /&gt;&lt;blockquote&gt;DELETE FROM PM1 WITH (ROWLOCK) WHERE KEYNAME = @P0&lt;/blockquote&gt;&lt;br /&gt;And we get a deadlock! Let's familiarize ourselves with some technical details and terms:&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Clustered and Non-Clustered Indexes&lt;/h4&gt;&lt;br /&gt;There are two database structures for this DDL.  One primary, clustered index, which has a key [ID]. And a secondary, non-clustered index, which has a key [KEYNAME].  A "clustered" index means that all of the data, the entire record, is stored in the leaf of the index.  It is still an index--it is still searchable by [ID], but when it gets to a leaf, the entire data record is physically stored there.  This is different from a secondary or non-clustered index in which the leaf of the index contains some kind of reference to the whole data record.  In our secondary index, the index is "searchable" by [KEYNAME], but in the leaves of the index, the "reference" is actually the primary key value (ID), which can be used then to "search" the primary, clustered index.  This is how databases use secondary indexes-- they find matching data with the index, then do a &lt;i&gt;bookmark lookup&lt;/i&gt;, which is a join, into the clustered index to get the rest of the data.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Seeking vs Scanning&lt;/h4&gt;&lt;br /&gt;There are different actions the engine can perform on these structures: the engine can &lt;b&gt;scan&lt;/b&gt; the index, in which case it will start somewhere (often the beginning) and start reading each record in order.  As these indexes a B+-Trees, the leaf level is a doubly linked list.  Thus, from a single leaf node, you can read sibling records &lt;i&gt;in order&lt;/i&gt; without having to go "up and down" the index levels.  Scanning is expensive as it is an O(n) operation which degrades as the number of records increases.  &lt;br /&gt;&lt;br /&gt;The engine can &lt;b&gt;seek&lt;/b&gt; the index, in which case it will use a binary search to locate a leaf node (record).  This is much preferable as it is a O(log n) operation-- and the base of that log is very high (the number of records per page).  Thus seeking is why we put indexes on tables-- to take advantage of the "quick" searching.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Getting Information about your deadlock&lt;/h4&gt;&lt;br /&gt;In Microsoft SQL Server (2005+) there is a useful report that can give you good details about your deadlock.  Without this information, it's very difficult to understand what's really happening.  You can get this information two ways &lt;ul&gt;&lt;li&gt;SQL Server Profiler, capturing the Deadlock event&lt;/li&gt;&lt;li&gt;Enable Trace Flag 1222&lt;/li&gt;&lt;/ul&gt; I find it generally useful to enable T1222 as a startup parameter for SQL Server, so that whenever a deadlock occurs, it dumps the details to the SQL Server error log file.  This is great, and incurs negligible performance overhead.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Trace 1222 Output&lt;/h4&gt;&lt;br /&gt;Let's look at the 1222 output and see what actually happened.  There are two sections to the 1222: (1) the processes involved, (2) the wait-for graph, which shows which locks are acquired which are trying to be acquired.  Here is an abbreviated 1222 for this deadlock:&lt;br /&gt;&lt;br /&gt;&amp;lt;deadlock victim="process3dcab08"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;process-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;process &lt;font color="blue"&gt;id="process3dcab08"&lt;/font&gt; ...&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;executionStack&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;frame ... &amp;gt;DELETE FROM PM1 WITH (ROWLOCK) WHERE AND KEYNAME =  @P0 &amp;lt;/frame&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/executionStack&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;inputbuf&amp;gt;(@P0 nvarchar(4000))DELETE FROM PM1 WITH (ROWLOCK) WHERE KEYNAME = @P0 &amp;lt;/inputbuf&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/process&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;process &lt;font color="green"&gt;id="process3de1198"&lt;/font&gt; &amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;... (same as previous process) ...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/process&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/process-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;resource-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;keylock ... objectname="DB1.dbo.PM1" indexname="PM_IDidx" ... mode="X" ... &amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="green"&gt;owner id="process3de1198" mode="X"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="blue"&gt;waiter id="process3dcab08" mode="U" requestType="wait"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/keylock&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;keylock ... objectname="DB1.dbo.PM1" indexname="PM_IDidx" ... mode="X" ... &amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="blue"&gt;owner id="process3dcab08" mode="X"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="green"&gt;waiter id="process3de1198" mode="U" requestType="wait"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/keylock&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/resource-list&amp;gt;&lt;br /&gt;&amp;lt;/deadlock&amp;gt;&lt;br /&gt;&lt;br /&gt;The process-list section describes all of the processes (concurrent executions) involved.  I have colored one blue and one green to help distinguish them.  So as we see each is holding an X (exclusive) lock, which the other is trying to get a U (update) lock for.&lt;br /&gt;&lt;br /&gt;The database must hold an X lock before actually modifying a record, thus we expect this to be involved.  However, a U lock is unexpected.  Update locks are an "in between" lock, which are used by the database when &lt;b&gt;scan&lt;/b&gt;ning for &lt;i&gt;potential&lt;/i&gt; records that need to be updated.  In this case, a U lock is acquired before checking the value to see if it matches your WHERE clause.  If it does, then the U is "upgraded" to an X lock, if not, then it is released.  However, our WHERE claue has KEYNAME = @P0, which is indexed.  Thus, we shouldn't be &lt;b&gt;scan&lt;/b&gt;ning anything-- we should be &lt;b&gt;seek&lt;/b&gt;ing on the index.  When seeking-- the database goes exactly to the records which qualify, and thus X locks are requested to begin with.  Thus, we expect to see only X locks from the statements above.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;So what happened?&lt;/h4&gt;&lt;br /&gt;The answer is subtle: look at the input buffer for the delete statement:&lt;br /&gt;&lt;br /&gt;&amp;lt;inputbuf&amp;gt;(&lt;font color="red"&gt;@P0 nvarchar(4000)&lt;/font&gt;)DELETE FROM PM1 WITH (ROWLOCK) WHERE KEYNAME = @P0 &amp;lt;/inputbuf&amp;gt;&lt;br /&gt;&lt;br /&gt;The parameter for the delete statement was declared by the driver as &lt;b&gt;nvarchar&lt;/b&gt;, but if you look back at the DDL, you will see that the KEYNAME column is declared as &lt;font color="red"&gt;varchar&lt;/font&gt;.  &lt;br /&gt;&lt;br /&gt;The NVARCHAR datatype is a unicode text datatype, whereas VARCHAR is non-unicode, 8-bit character datatype.  The engine cannot directly compare an nvarchar value to a varchar value-- it must use a conversion.  This conversion makes the where clause predicate not &lt;i&gt;sargable&lt;/i&gt;, and that disallows the optimizer from &lt;b&gt;seek&lt;/b&gt;ing on it.  Instead it scans the whole index, one record at a time, and for each one, requests a U lock, converts and checks the value, releases the U lock.  Thus, for &lt;b&gt;every&lt;/b&gt; delete of a single record, every record must be checked with an expensive, conflicting lock.  And as seen in our case, two concurrent deletes happen to be checking and ran into each other.  &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;The solution&lt;/h4&gt;&lt;br /&gt;So why did the driver do that? Well this was a Java application, and in Java strings are unicode by default. By default, the driver binds strings to their unicode counterpart.  However, there is a setting on the driver's connection url, which instructs the driver to bind to non-unicode instead.  The key is that you want to ensure that the driver is sending parameters the same as the underlying schema is defined.  For JTDS the setting is &lt;b&gt;sendStringParametersAsUnicode&lt;/b&gt; and it defaults to TRUE.  Thus, if your schema is defined with all VARCHARs and you are using them in indexes, then you want to ensure this property is set to &lt;b&gt;FALSE&lt;/b&gt; in your connection url.  Here is an example of a connection url that forces the driver to send them as varchar instead of nvarchar: &lt;br /&gt;&lt;br /&gt;jdbc:jtds:sqlserver://mssql01:1433/DB1;sendStringParametersAsUnicode=false&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Deadlock II&lt;/h2&gt;&lt;br /&gt;This deadlock is a little less common, but interesting, because it also has an "unexpected" wait-for graph in the 1222 output.  The schema was jBPM, which may be familiar. I need to see if someone has already fixed this in jBPMs delivered schema. &lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Trace 1222 Output&lt;/h4&gt;&lt;br /&gt;First, let's look at the 1222 trace:&lt;br /&gt;&lt;br /&gt;&amp;lt;deadlock victim="process3de0868"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;process-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;process &lt;font color="blue"&gt;id="process3deb08"&lt;/font&gt; ...&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;inputbuf&amp;gt;(@P0 bigint,@P1 int)delete from JBPM4_SWIMLANE where DBID_= @P0 and DBVERSION_= @P1&amp;lt;/inputbuf&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/process&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;process &lt;font color="green"&gt;id="process3de0868"&lt;/font&gt; ...&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;... same as other process ...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/process&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/process-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;resource-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;keylock ... objectname="dbo.JBPM4_TASK" indexname="PK__JBPM4_TASK__2BFE89A6" mode="X" ...&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="blue"&gt;owner id="process3deb08" mode="X"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="green"&gt;waiter id="process3de0868" mode="S" requestType="wait"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/keylock&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;keylock ... objectname="dbo.JBPM4_TASK" indexname="PK__JBPM4_TASK__2BFE89A6" mode="X" ...&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="green"&gt;owner id="process3de0868" mode="X"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/owner-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&lt;font color="blue"&gt;waiter id="process3deb08" mode="S" requestType="wait"&lt;/font&gt;/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/waiter-list&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/keylock&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/resource-list&amp;gt;&lt;br /&gt;&amp;lt;/deadlock&amp;gt;&lt;br /&gt;&lt;br /&gt;So we have two workers executing a delete by primary key... and they're deadlocking on &lt;i&gt;S&lt;/i&gt; locks!? An S (shared) lock is used when the database needs to get a consistent read on something.  Thus, by default, when you execute SELECT statements, shared locks are being issued under the covers to ensure read consistency (w.r.t the isolation level you're running at).&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;So what happened?&lt;/h4&gt;&lt;br /&gt;There is a hint in the wait-for graph that shows the problem:  Look at the keylock that is being contended-- the indexname is PK__JBPM4_TASK__2BFE89A6.  This is an auto-generated name for the primary, clustered index for the JBPM4_TASK table.  We're deleting from the SWIMLANE table, so why would there be any locks on the TASK table?  The TASK table has a foreign key reference (field swimlane_) to the SWIMLANE table.  Thus, when you delete a SWIMLANE record, the database must ensure that there are no records referring to the SWIMLANE record.  If there are, then the delete cannot proceed, because it would be a foreign key constraint violation.  To do this check, the database needs to find records in the JBPM4_TASK table WHERE swimlane_ = &lt;i&gt;id of swimlane being deleted&lt;/i&gt;.  This read will be done using S locks to ensure consistency.  Unfortunately, the JBPM4_TASK table does &lt;b&gt;NOT&lt;/b&gt; have a secondary index on the swimlane_ column.  Thus, the only option the database has is to &lt;b&gt;scan&lt;/b&gt; the entire table by primary, clustered index.  Thus, for each record, a S lock must be acquired, the record evaluated for the WHERE clause, and then the S lock is released.  Each one of these is a deadlock opportunity if there are concurrent operations touching the TASK table.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;The Solution&lt;/h4&gt;&lt;br /&gt;Adding an index on the swimlane_ column in the TASK table will reduce the &lt;i&gt;n&lt;/i&gt; deadlock opportunities to 1, which is a significant improvement-- not to mention the performance improvement.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;The Overall Theme&lt;/h2&gt;&lt;br /&gt;While these are very different deadlocks, the theme is the same: unintended &lt;b&gt;scan&lt;/b&gt;s.  Scans are not only a performance problem, but a deadlock problem, as you increase the number of times that you request conflicting locks while holding locks.  Thus, take care when considering which indexes to create-- its a tricky problem with many factors.&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-5354496707233181473?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/5354496707233181473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/10/deadlock-two-ways-microsoft-sql-server.html#comment-form' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/5354496707233181473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/5354496707233181473'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/10/deadlock-two-ways-microsoft-sql-server.html' title='Deadlock two ways (Microsoft SQL Server)'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-368561233815868012</id><published>2010-10-11T16:39:00.007-05:00</published><updated>2010-10-11T21:43:09.171-05:00</updated><title type='text'>Some JIRA / GreenHopper Love</title><content type='html'>This will be another brief, non-technical, un-informative post to rant..but in a positive way! I actually will post something informative and useful one day ;-)&lt;br /&gt;&lt;br /&gt;A team member and I have been using JIRA and recently started using the GreenHopper plug-in.  The plug-in advertises that it is "agile" focused.  I have always been extremely satisfied with Atlassian products, but this is something special.  They really have done a great job in usability with GreenHopper.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The "boards" present a nice, intuitive, usable visualization of a part of the project.  &lt;li&gt;Every aspect of the "boards" visualization (color coding, progress bars, in-place editing) reinforces with high affordance the functionality of that view.&lt;/li&gt;&lt;li&gt;The tools gets out of the way as much as possible in terms of navigation and functionality. Its intuitive. I find that I spend 95% of my time just on the Task board. I never need to leave.&lt;/li&gt;&lt;li&gt;Using the "boards" is fun!&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;For example, from the "task" board, I can open bugs, assign them to other people, begin progress, etc.  This is my "specialized" view that I, a developer, use to see my tasks and at-a-glance get a view of the whole sprint.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_CNfYytZgqGU/TLOHxxL3X4I/AAAAAAAAABA/h-jKYdgGqL8/s1600/jira1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 234px;" src="http://4.bp.blogspot.com/_CNfYytZgqGU/TLOHxxL3X4I/AAAAAAAAABA/h-jKYdgGqL8/s400/jira1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5526910456800173954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here are some noteworthy features of this visualization:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There are three columns for this sprint: To Do, In Progress, Done.  You just drag a card between the columns to start progress or resolve as done.  Just as you would with a card on a cork board (intuitive metaphor).&lt;/li&gt;&lt;li&gt;You can switch projects and sprints/versions by clicking the down arrows next in the headers&lt;/li&gt;&lt;li&gt;You can add bugs, features, etc. for this sprint just by clicking the button for that issue (right under the filter icon).&lt;/li&gt;&lt;li&gt;You can add comments to the issue simply or bring up a light box with the entire JIRA issue (see below).&lt;/li&gt;&lt;li&gt;There is a nice progress bar that shows the overall progress for the entire sprint.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_CNfYytZgqGU/TLOLHgACiUI/AAAAAAAAABI/mM7X25vgB5o/s1600/jira2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 222px;" src="http://2.bp.blogspot.com/_CNfYytZgqGU/TLOLHgACiUI/AAAAAAAAABI/mM7X25vgB5o/s400/jira2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5526914128679176514" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When a new sprint comes around you can check out the "planning" board, where you can drag and drop cards to different releases or sprints.  Also, there is a "chart" board which summarizes various charts (burndown rate, etc.), and a "released" board which provides metrics on completed sprints and releases.&lt;br /&gt;&lt;br /&gt;This is just a little bit of why I like GreenHopper.  I have just been so pleased with it that I wanted to share.  I have used bugzilla, plain JIRA, and Team Foundation in the past.  Team Foundation I know has much of the same (if not more) reporting than GreenHopper, but I never saw a visualization for me, the developer, that was as useful (defined by power/weight ratio) as what GreenHopper provides in the Planning Boards.  Its the subtle simplicity and usability that I think makes GreenHopper so great-- not just sheer functionality.&lt;br /&gt;&lt;br /&gt;If anyone actually reads this I would be interested to hear others' opinions about tools-- in particular other "killer features" like GreenHoppers planning boards.&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-368561233815868012?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/368561233815868012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/10/some-jira-greenhopper-love.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/368561233815868012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/368561233815868012'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/10/some-jira-greenhopper-love.html' title='Some JIRA / GreenHopper Love'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_CNfYytZgqGU/TLOHxxL3X4I/AAAAAAAAABA/h-jKYdgGqL8/s72-c/jira1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-6974272023543570859</id><published>2010-09-20T19:53:00.004-05:00</published><updated>2010-09-20T20:18:13.762-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='api design'/><title type='text'>Naming Matters</title><content type='html'>This title is kind of a two-for: "Naming matters" as in matters pertaining to naming and naming &lt;i&gt;matters&lt;/i&gt; as gee golly-- names really do make a difference!  So hurray for homonyms and making a neat opening to what is likely to be a boring post :-)  (Have I hooked you to read further? dratz...)&lt;br /&gt;&lt;br /&gt;API design "theory" is curious, because it seems to take a back seat in the academic literature, and yet the economic impact of bad API design is so great to the "problem" of software development.  I guess the Software Engineering folk are fighting the good fight with code quality metrics, etc., but this is a hard topic, that I had never considered throughout my entire undergraduate career.&lt;br /&gt;&lt;br /&gt;Here's an example of an API design mistake in my opinion (albeit a simple one).  In Java, there are the following constants defined:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Integer.MIN_VALUE&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Long.MIN_VALUE&lt;/b&gt;&lt;/li&gt;&lt;li&gt;etc.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;These define the lowest value that can be represented by this datatype.  Thus, for example, &lt;b&gt;Integer.MIN_VALUE&lt;/b&gt; is -2147483648 given that its 4 bytes and uses a 2-s complement representation, etc.&lt;br /&gt;&lt;br /&gt;These are used all over the place as sentinels.  For example, a typical implementation of Dijkstra's shortest path algorithm initializes every node to a shortest distance of &lt;b&gt;Integer.MAX_VALUE&lt;/b&gt;, and as the algorithm progresses, any value will (likely) be less than MAX_VALUE.  Thus, it's a natural way to construct the algorithm without having to code a bunch of fragile "special cases" (adding to your cyclomatic complexity).&lt;br /&gt;&lt;br /&gt;So what do you think &lt;b&gt;Double.MIN_VALUE&lt;/b&gt; returns?&lt;br /&gt;&lt;br /&gt;Well as a bug that I coded last week demonstrates, it does *not* return the minimum value of the range of values that can be represented in an instance of the Double data type.  That would be &lt;i&gt;ludicrous&lt;/i&gt; (this is me being sarcastic).&lt;br /&gt;&lt;br /&gt;No, instead it returns the smallest &lt;i&gt;positive&lt;/i&gt; value.  You need to use &lt;b&gt;Double.NEGATIVE_INFINITY&lt;/b&gt; for the smallest, *cough* &lt;i&gt;minimum&lt;/i&gt; *cough* value.&lt;br /&gt;&lt;br /&gt;Note that in C#/.NET Double.MinValue is the &lt;i&gt;minimum&lt;/i&gt; value (i.e. negative number).  I wonder what it is in limits.h...&lt;br /&gt;&lt;br /&gt;So what's up Java?!  I wonder how many man hours of time have been wasted on this pitfall since Java's inception?  Can we ask Guy Steel and Bill Joy for a check?  Just kidding... &lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-6974272023543570859?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/6974272023543570859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/09/naming-matters.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/6974272023543570859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/6974272023543570859'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/09/naming-matters.html' title='Naming Matters'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-4386142391089545721</id><published>2010-05-08T21:27:00.003-05:00</published><updated>2010-05-08T22:16:54.856-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='opinions'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>Save the commentary, please</title><content type='html'>I am working on a thesis right now on optimizing data structures and algorithms in database systems.  I am doing my implementation for the first part in MySql, and will be augmenting one of the storage engines (maybe InnoDB, maybe something else) to test out some of the ideas in my paper.&lt;br /&gt;&lt;br /&gt;Anyways, I picked up &lt;a href="http://www.amazon.com/Understanding-MySQL-Internals-Sasha-Pachev/dp/0596009577"&gt;Understanding MySql Internals&lt;/a&gt; to use as a reference for some of the major components of the software.  The technical information is pretty good, and its nice to get a discourse on such a large code base.&lt;br /&gt;&lt;br /&gt;However, something bothers me about the book.  Riddled throughout the technical details are little dollops of over-embellished praise for Monty Wideneous, the creator of MySql.  Now, don't get me wrong-- I go a bit ga-ga for rockstar developers like Monty, Rod Johnson, Crazy Bob Lee, etc., but not in a &lt;i&gt;technical book&lt;/i&gt;.  It's just awkward to be reading about architectual decisions for the threading model in MySql and then read this:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;As far as the programming skills were concerned, they were not lacking at all.  Just as a good rider becomes one with the horse, Monty had become one with the computer.  It pained him to see system resources wasted.  He felt confident enough to be able to write virtually bug-free code.&lt;/blockquote&gt;(p. 109)&lt;br /&gt;&lt;br /&gt;Hyperbole such as "Monty had become one with the computer" can be excluded from technical books in my opinion.&lt;br /&gt;&lt;br /&gt;Here's another example of the Monty "love-fest":&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;...perhaps building on his God-given talent, Monty developed a habit and ability to write very efficient code naturally.  He also developed, or perhaps was gifted from the start, with an unusually acute vision of what needed to be done to the code to make it useful in future development--without knowing in advance much detail about what that future development would be&lt;/blockquote&gt;(p. 1)&lt;br /&gt;&lt;br /&gt;He could've removed this entire paragraph and we would know nothing less, except that the author clearly wants to be on Monty's Christmas card list.  So, technical book authors: &lt;b&gt;save the commentary, please&lt;/b&gt;.  ...or maybe just keep it out of the main text and include an optional, opinion-based companion workbook!  &lt;br /&gt;&lt;br /&gt;For example, accompanying the section on Threads vs Forked Processes, there could be a multiple choice question:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Why did Monty Widenious choose a thread model instead of a forked process model for MySql?&lt;/i&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Large amount of shared data between workers&lt;/li&gt;&lt;li&gt;Greater overhead in context switching processes over threads&lt;/li&gt;&lt;li&gt;Greater overhead in creating a forked process over creating a thread&lt;/li&gt;&lt;li&gt;Monty is a man-god, who is incapable of writing bugs in source code&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-4386142391089545721?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/4386142391089545721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/05/save-commentary-please.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/4386142391089545721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/4386142391089545721'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/05/save-commentary-please.html' title='Save the commentary, please'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-8662065073299946098</id><published>2010-04-29T22:11:00.003-05:00</published><updated>2010-04-29T23:15:09.864-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='leaky'/><category scheme='http://www.blogger.com/atom/ns#' term='complaining'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><title type='text'>Wow...erm...your database is leaking</title><content type='html'>It's no secret that some of the traits that go into making good software developers are: problem solving, analytical, and abstraction skills.  As we write code and design a software system, we exercise all of these skills -- in particular: &lt;b&gt;abstraction&lt;/b&gt;.  Abstractions allow us to build larger and more complex systems without needing bigger and bigger brains.  We can reason about the interactions of components at various levels, and only focus on the details germane to the topic at the time.&lt;br /&gt;&lt;br /&gt;Operating systems provide various abstractions that we use everyday: various models of how to "do work" (processes and threads), how to exchange information (multi-threaded programming primitives, etc.).  We leverage complex data structures from libraries.  We wouldn't want to re-write a hash table every time we need dictionary lookup semantics, or drop into assembly to write CAS instructions to re-implement mutexes and spinlocks every time we write a multi-threaded program.  So *high five* to &lt;b&gt;abstraction&lt;/b&gt;-- you're a good friend to us dumb humans.&lt;br /&gt;&lt;br /&gt;Yet... there is a problem.  In the perfect world in our cerebral cortex, things fit together nicely, and we gloss over real world needs and various "cross-cutting concerns".  As such, there is the famous &lt;i&gt;principal of leaky abstraction&lt;/i&gt;, which states that there are no perfect abstractions: all abstractions leak-- i.e. all abstractions expose some part of their underlying implementation.  The amount of "leakiness" can be a quality metric.  I would suspect that "leakiness" is correlated with complexity, and it is certainly correlated with how many resources are involved in the implementation.&lt;br /&gt;&lt;br /&gt;For example, how leaky are mutex synchronization primitives?  On the surface they sound simple! You try to acquire the mutex and get blocked until you get what you want.  A nice abstraction, very useful.  But then there are these pesky details of lock fairness, thread affinity, etc. that complicate the elegant abstraction.  They typically rear their head through "cross-cutting concerns" such as performance.  And this is key to the issue-- why can't there be a perfect abstraction?  Because at some point there is a *finite* set of resources that are going to give a quick dose of "reality" to these "head in the clouds", "flowers and kittens" abstractions.  And where abstract, pure concepts meet reality-- finite constraints start to paint a different picture.&lt;br /&gt;&lt;br /&gt;So as developers, we deal with this by straddling the fence: we design in the world of abstractions, but maintain some "implementation" knowledge, and then TEST TEST TEST (for performance and other non-functional requirements).  And we pick up little best practices about thread affinity, processor cache coherence, scheduling fairness, IO access patterns, etc. that let us meet these goals.&lt;br /&gt;&lt;br /&gt;But in my experience with different teams and colleagues, something funny happens when databases are brought into the pictures.  They are almost always treated as a black box.  "Oh thats the databases job", "oh well the database just sucks", "databases suck".  I have encountered a number of people who choose to write their own little mini-databases instead.  They have no problem "breaking the abstraction" to think about file locks, optimizing reads/writes to reduce disk head seeks, etc.-- but they hold database technology to some higher standard; they pretend databases are a perfect abstraction!&lt;br /&gt;&lt;br /&gt;So yes, databases are very leaky.  They are leakier than many other programming tools, and this seems natural given the complexity.  But they bring a slew of features, a slew of research, and a lot of production time with them.  Thus, we as developers should be interested in understanding this tool, its leakiness, and how to effectively use it, just as we would anything else (O/S, hard disks, etc.).&lt;br /&gt;&lt;br /&gt;Where do databases leak the most?  Here's a brief list of various technical nuggets about popular database implementations that are important to at least have in the back of your mind when you're designing something with a database:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Concurrency is hard!&lt;/li&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Its multi-threaded programming!  Databases aren't magic-- they have to employ the same synchronization as the rest of the world to use a shared memory model of parallelism.  Thus read up on isolation levels, read up on optimistic and pessimistic locking, and don't rely on "magic" as an answer for how databases do what they do.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Transactional consistency is hard!&lt;/li&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Understand why ACID properties are important, how they make reasoning about complex interactions simple, and why it is almost always economically impossible to create a &lt;b&gt;robust&lt;/b&gt;, complex system without some transaction abstraction&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Searching for data is hard!&lt;/li&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Searching problems in computer science make up a major chunk of the literature for the last thirty years.  &lt;i&gt;Oh, you put your data into a table, didn't understand what you should index, and now you blame the database for performing slowly!?&lt;/i&gt;  Understand the difference in clustered and non-clustered indexes.  Understand the concept of &lt;i&gt;selectivity&lt;/i&gt;, and learn how to evaluate which queries are sucking the life out of your application.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;At some point, I'll actually put something technical on this blog :-)&lt;br /&gt;&lt;br /&gt;&lt;i&gt;*kicks soap box to the corner of the room*&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-8662065073299946098?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/8662065073299946098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/04/wowermyour-database-is-leaking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/8662065073299946098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/8662065073299946098'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/04/wowermyour-database-is-leaking.html' title='Wow...erm...your database is leaking'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-6617536415128497016</id><published>2010-01-05T15:35:00.005-06:00</published><updated>2011-04-13T10:19:51.103-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='reading list'/><title type='text'>Spring Reading List</title><content type='html'>Wow...I really haven't posted regularly, have I?  I'd like to catalog some things for my own reference, and to come back in years and chuckle at my musings and naiveté.  This leads naturally into a topic: knowledge and learning.&lt;br /&gt;&lt;br /&gt;I am an avid reader.  I don't really enjoy much fiction, but I love computer science and technology books.  I have a bit of an ...erm... addiction (some would say) to Amazon Marketplace.  If we take a trip in the 'way back' machine to 2001ish, we will see a younger (still in High School) Steve, delighted to receive his first Amazon Marketplace shipment: &lt;a href="http://www.amazon.com/Advanced-Visual-Basic-Techniques-Everyday/dp/0201707128/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1267134110&amp;amp;sr=8-1"&gt;Advanced Visual Basic 6: Power Techniques for Everyday Programs&lt;/a&gt; and &lt;a href="http://www.amazon.com/Effective-Visual-Basic-Improve-Applications/dp/0201704765/ref=sr_1_2?ie=UTF8&amp;amp;s=books&amp;amp;qid=1267134093&amp;amp;sr=8-2"&gt;Effective Visual Basic: How to Improve your VB/COM+ Applications&lt;/a&gt;.  &lt;i&gt;Once you have stopped laughing at the thought of "Effective" or "Advanced" Visual Basic, please continue&lt;/i&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_CNfYytZgqGU/S4b6FLkEPiI/AAAAAAAAAAw/f1QVEmo0OVs/s1600-h/IMAGE_165.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: both; cursor: pointer; width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_CNfYytZgqGU/S4b6FLkEPiI/AAAAAAAAAAw/f1QVEmo0OVs/s320/IMAGE_165.jpg" alt="" id="BLOGGER_PHOTO_ID_5442312166634176034" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here are two of my bookshelves now.  I can't honestly claim that I have read all of them cover to cover, but I always read at least a few chapters of each, and note which sections I want to come back to.  Many, though, I have read all the way through-- for example, if you haven't read Brian Goetz's &lt;a href="http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1267134676&amp;amp;sr=8-1"&gt;Java Concurrency in Practice&lt;/a&gt;, then you are missing out!&lt;br /&gt;&lt;br /&gt;Here are some of my favorite books:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/ref=pd_sim_b_1"&gt;Effective Java&lt;/a&gt; by Josh Bloch.  A collection of pragmatic patterns, and more importantly sound reasoning as to &lt;i&gt;why&lt;/i&gt; they are recommended.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1267134676&amp;amp;sr=8-1"&gt;Java Concurrency in Practice&lt;/a&gt; - excellent overview of the two most important aspects of multi-threaded programming: memory consistency and mutual exclusion.  People seem to forget about the first one.  Once you finish this book, please go read William Pugh's information on &lt;a href="http://www.cs.umd.edu/%7Epugh/java/memoryModel/index.html"&gt;JSR 133&lt;/a&gt;.  After that if you still don't understand what &lt;b&gt;volatile&lt;/b&gt; &lt;i&gt;really&lt;/i&gt; is, then repeat.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0321413091/ref=oss_product"&gt;Implementation Patterns&lt;/a&gt; by Kent Beck. He describes the book as a companion to Fowler's canoncial "Refactoring" text.  Implementation Patterns focuses on the 'small' decisions that we make every day as it pertains to design, code organization, and getting all of the '-ilities' working for us.  Reading this was like re-living the first few years of professional Java development-- Kent just did a good job 'formalizing' a vocubulary with which to discuss and evaluate these various 'design pressures' that enter our mind as we code.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0557078326/ref=oss_product"&gt;Real World Java EE Patterns&lt;/a&gt; by JEE guru Adam Bien.  I found this book wonderful.  In particular, his trade off analysis that accompanies each pattern is an excellent way to learn how he reasons about design.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/020161622X/ref=oss_product"&gt;The Pragmatic Programmer&lt;/a&gt; - this is a famous book and certainly worth the read.  I think it's a good litmus test for oneself.  I read this once before I started my career and found that I &lt;i&gt;learned&lt;/i&gt; a ton.  I thought all of the little gems of advice were new and useful.  I re-read (or skimmed) this recently, and found myself going "yep, I remember learning that the hard way".  I kind of view this as a way to guage the efficiency of the environment within which you work.  I have been lucky to work in an environment that properly incentivized writing good software.  Thus, I learned these lessons quickly and efficiently as part of my day to day work.  If you work in a lazy environment, you may never learn these lessons.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0596510047/ref=oss_product"&gt;Beautiful Code&lt;/a&gt; - this collection of essays is a great read.  It's goal (and it succeeds) is imparting &lt;i&gt;how&lt;/i&gt; great developers think.  These lessons help 'shape' the way in which we approach problem solving.  The entire "Beautiful" series is wonderful.  I have read them all, except the new "Beautiful Security".&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/ref=pd_sim_b_2"&gt;Java Generics&lt;/a&gt; - I am not recommending this book, unless you are (a) curious about some of the 'exotic' features of generics, (b) enjoy the theoretical aspects of language design (reification, contra/covariance, etc.) , or (c) want to understand the reasoning behind the design decisions that were made by the expert group.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/1430219025/ref=oss_product"&gt;SQL Server 2008 Query Performance Distilled&lt;/a&gt; - this is my favorite pick for SQL Server optimization.  Of course Delaney's Inside SQL volume on the matter is great too, but &lt;i&gt;Distilled&lt;/i&gt; is a little bit more bang for your buck.  I found more useful information crammed into a smaller text.  Once you read this (and all of the Inside SQL Server series) then you can graduate to &lt;a href="http://blogs.msdn.com/craigfr/"&gt;Craig Freedman's Blog&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Whew... well I know I am forgetting some.  I should probably edit this later after I return home to check any that I may have missed.  Another neat fact I learned in doing this is that I have had 137 separate orders with Amazon since 2004... most of which had multiple books :/  I need to seek group help or something...&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-6617536415128497016?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/6617536415128497016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/01/spring-reading-list.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/6617536415128497016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/6617536415128497016'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2010/01/spring-reading-list.html' title='Spring Reading List'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_CNfYytZgqGU/S4b6FLkEPiI/AAAAAAAAAAw/f1QVEmo0OVs/s72-c/IMAGE_165.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4471109663192942674.post-1323654971495732944</id><published>2009-09-05T12:36:00.001-05:00</published><updated>2010-02-25T15:35:25.241-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='Introduction'/><category scheme='http://www.blogger.com/atom/ns#' term='Interests'/><title type='text'>Welcome! Do you prefer a bold or mild brew?</title><content type='html'>Hello,&lt;br /&gt;&lt;br /&gt;My name is Steve.  I am currently an enterpise Java developer in a high volume transaction processing oriented domain.  We do a lot of 'batch' workloads, and so my day is filled with lots of interesting challenges.  I hope to catalogue some of these challenges here, and if I'm lucky, some more learned individuals on the internets will contribute.  My current interests are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;High volume, batch oriented, mixed workloads and how such workloads scale using traditional (DBMS) and non-traditional (grid, extreme transaction processing) architectures.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Database technology, specifically implementation with regards to concurrency&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Distributed computing including caching and data grids, computing grids, partitioning approaches, etc.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Problem solving, cognitive calisthenics, intelligence and learning.  Obviously, these are very important topics in Computer Science.  I am interested in these primarily in how they relate to developer productivity, but also am interested in them in the AI domain.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Economics of software engineering.  This includes SDLC processes, but currently focused on how teams and processes scale given specialization, project size, etc.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I feel like I am not doing a good job describing my interests. :-(  I, like most, have a lot of them!&lt;br /&gt;&lt;br /&gt;I know that this is just a metaphorical 'drop in the bucket' of technology blogs, but I am going to try to blog about things that I haven't seen on other blogs.  This is spurious from the start, because obviously, I have only read a tiny subset of existing blogs... but I'll give it a go, none the less.&lt;br /&gt;&lt;br /&gt;Upcoming topics (these first are very database centric):&lt;br /&gt;&lt;br /&gt;- SQL Server deadlock hell (or how I learned to stop worrying and love the lock)&lt;br /&gt;- Fragmentation, Batch Inserts, and Primary Keys...oh my!&lt;br /&gt;&lt;br /&gt;Steve&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4471109663192942674-1323654971495732944?l=manycupsofcoffee.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://manycupsofcoffee.blogspot.com/feeds/1323654971495732944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://manycupsofcoffee.blogspot.com/2009/09/welcome-do-you-prefer-bold-or-mild-brew.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/1323654971495732944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4471109663192942674/posts/default/1323654971495732944'/><link rel='alternate' type='text/html' href='http://manycupsofcoffee.blogspot.com/2009/09/welcome-do-you-prefer-bold-or-mild-brew.html' title='Welcome! Do you prefer a bold or mild brew?'/><author><name>Steve Ash</name><uri>https://profiles.google.com/114351809892455116496</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh4.googleusercontent.com/-1Yr_2dAqq-s/AAAAAAAAAAI/AAAAAAAABC4/ROflGnCKQUg/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry></feed>
