Process with States

I implemented this again today, and realized it was a pattern that I fall back on regularly.

When dealing with a system with several states, and actions to be performed at each state, I usually break things up into a state diagram.  Inevitably, I end up with a “State” field, the the question becomes what to put in the state field.

Take an example of sending an invite to a user to come in and use an app.

What I do not do:

Alter Table Invitation

add state int not null default(1)

 

enum State { Start = 1, Email = 2, Consumed=3, Expired=4 }

What I do (approximately):

alter table Invitation add
	state varchar(25) null, 
	stateError bit(1) default(0) null, 
	stateTimestamp DateTime default(getdate()) not null

public static class InvitationState { 
   public const string SendEmail="SendEmail";
   public const string WaitInbound="WaitInbound"; 
   public const string InviteConsumed="InviteConsumed"; 
   public const string InviteExpired="InviteExpired";
}

Why?

  • It is my experience that for any flow, a future spec will come up with will add new states in the middle of that flow.  If you go with an int value, you end up with the flow being 1->9->5->6.   By going with a varchar, its much more obvious when doing a select from the db, and it allows for the addition of new states.
    • A previous employer loved using CHAR(4) and state names like “STRT”,”MAIL”,”CONS”,”EXPR” because Oracle stored the Char(4) like an int.   Very similar to Apple’s Resource Fork’s from back in the day.
    • I’m not so worried about the speed difference it makes.  I feel that the flexibility it gives makes up for any performance loss.
  • Either we have an action state, named with a “Verb Noun”, or we have and end state, named with “Noun Verb”.
  • The state names are based on what IS happening or SHOULD happen to the row, not what has already happened, except for the end states.
  • For any action state, if the attempt at performing that action fails, I set the stateError bit to 1.  This lets me know I need to do something to fix that; when its fixed, I can change the stateError back to 0.
    • In large batch systems, I write the console based action-taker with an override switch:  to work on a specific Id, even if the errorState=1; and to do it in a transaction but not commit the transaction.  
    • This lets me do test-things with rows that failed, while the main batch system processes all the other non-errored rows.   Once I get it to working, I tell it to go ahead and commit, and the system continues on its merry way.
  • The system only deals with the states that it knows.  This lets the technical OPS team (whoever that is) use the state field for its out-of band processes, stuff like setting the state to ‘Ken’, for example.   Because Ken is the man who knows what is up with those rows.
  • In really large critical systems, there’s an audit table and a trigger that auto-inserts any changes to the audit table. 

The result of this is, I get Christmas cards in the mail from former clients with phrases like “Still amazed by your code!!!”.   🙂

Hope this helps. 

Tagged with: ,
Posted in Code

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories
Tags
.net 3d 3d-printing 4k abc15 algorithms ames android anonymous types asp.net audio editing aws backup basecamp beatunes biorhythm bittorrent blender blog boston marathon bpm c# caffeine campfire candycrush car carmax charity chiropractor cities-skylines clog clone codelouisville codepalousa coding coffee collaboration color run ComputerElbow ComputerVision configuration consulting cooking crash course crashplan crestwood cycling dabda dan dapper DataSet ddl diabetes dictation dotnetcore dotnetmud downtown e-cycling elite excel exercise expiration facebook feature-branching firefall flipflops Flow FL Studio focus food forecastle fortresscraft franklinplanner gadgets game-design games git github google docs google maps gopro gps grandpa greenshot hack half marathon headless health heart rate hiren ignew integration testing interop inventory ios ipad itunes javascript jobs karma kdf keyboards keys kittens lamont laptop lavalamp lego life lifehack linq linqtotwitter linux los angeles louisville mandelbulber massage therapy mastery-teaching maths merge metformin Minecraft miniature modeling monitor mud muhammad ali institute music mvc mycartracks netfabb nexus10 node nostalgia nutrition nwipe oldham county grand slam opal openjscad openscad owin pacedj paper mockup pepakura performance photoscan politics pomodoro postgresql powershell premiere prius process product-management project-management qa resharper review rmi roman road 5k RSI rubiks running samsung 700t sandals schedule scooter scribblelive selenium service shapeways sleep slic3r sneakersync snot software software-engineering solidoodle soylent spacegame speaking sql sqlite SSDT SSIS standing state-machine stayfocusd stonehearth sunset tablet teaching team teamcity teamtreehouse terraform testing tfs time timelapse torque touch tracks trs80 Tuple tutor twitter ubuntu unit testing utilities video video editing visual studio vscode vsvim warp stabilizer windows 8 windows home server wordpress wpf xml
Archives
%d bloggers like this: