Erlang Thursday – c:pid/3

Today’s Erlang Thursday is a short one on c:pid/3.

c:pid/3 takes the three different parts of a pid as its arguments, and returns a constructed Pid type corresponding to those values.

We’ll call self to get a pid that we know is good, and we can use that to compare the result of calling c:pid/3.

self().
% <0.42.0>
c:pid(0, 42, 0).
% <0.42.0>
self() =:= c:pid(0, 42, 0).
% true

Why is this useful? Sometimes when inspecting what is going on in a live system there are certain calls in Erlang that expect a pid() type, and not just the pid numbers.

c:regs().
%
% ** Registered procs on node nonode@nohost **
% Name                  Pid          Initial Call                      Reds Msgs
% application_controlle <0.7.0>      erlang:apply/2                     463    0
% code_server           <0.19.0>     erlang:apply/2                  128774    0
% erl_prim_loader       <0.3.0>      erlang:apply/2                  163760    0
% error_logger          <0.6.0>      gen_event:init_it/6                220    0
% file_server_2         <0.18.0>     file_server:init/1                 448    0
% global_group          <0.17.0>     global_group:init/1                 59    0
% global_name_server    <0.13.0>     global:init/1                       51    0
% inet_db               <0.16.0>     inet_db:init/1                     206    0
% init                  <0.0.0>      otp_ring0:start/2                 3398    0
% kernel_safe_sup       <0.27.0>     supervisor:kernel/1                 58    0
% kernel_sup            <0.11.0>     supervisor:kernel/1              49109    0
% rex                   <0.12.0>     rpc:init/1                          35    0
% standard_error        <0.21.0>     erlang:apply/2                       9    0
% standard_error_sup    <0.20.0>     supervisor_bridge:standar           41    0
% user                  <0.24.0>     group:server/3                      36    0
% user_drv              <0.23.0>     user_drv:server/2                17940    0
%
% ** Registered ports on node nonode@nohost **
% Name                  Id              Command
% ok
erlang:is_process_alive(c:pid(0, 6, 0)).
% true

So let’s see what happens when we try to feed it something to break it, but in a meaningful way.

c:pid(0, 0, 0).
% <0.0.0>
c:pid(111110, 0, 1111110).
% ** exception error: bad argument
%      in function  list_to_pid/1
%         called as list_to_pid("<111110.0.1111110>")
%      in call from c:pid/3 (c.erl, line 424)

So it looks like the pid <0.0.0> is a valid pid, but when we throw it something else, we see it is trying to call list_to_pid.

So let’s take a quick look at list_to_pid.

erlang:list_to_pid("<0.42.0>").
% <0.42.0>
c:pid(0, 42, 0) =:= erlang:list_to_pid("<0.42.0>").
% true

So it looks like c:pid/3 is a wrapper function around list_to_pid that builds the 3 parts of a pid into a string, and then gets a pid() type from that call.

–Proctor

,

Leave a Comment

Ruby Tuesday – String#casecmp

Today’s Ruby Tuesday covers String#casecmp.

String#casecmp is a kind of oddly named method, as what it does is a case insensitive string comparison.

The return value of String#casecmp is either -1, 0, or 1, depending on if the item casecmp is being called on is less than, equal or greater than the string passed in as an argument.

"foobar".casecmp("FOOBAR")
# => 0
"abcdeft".casecmp("ABCDEFG")
# => 1
"abcdefg".casecmp("ABCDEFG")
# => 0
"A".casecmp("b")
# => -1
"a".casecmp("B")
# => -1
"z".casecmp("A")
# => 1
"Z".casecmp("a")
# => 1
"z" <=> "A"
# => 1
"A" <=> "Z"
# => -1
"a" <=> "Z"
# => 1
"Z" <=> "a"
# => -1

This can be handy if you are trying to match two strings by doing a downcase or upcase on the strings, as well as being more clear about what you are trying to accomplish with the comparision.

You can also take advantage of using String#casecmp if you even need to do sorting of items by their name regardless of case.

["foo", "a", "Z", "Foo", "buzz", "FOO"].sort do |a, b|
  result = a.casecmp(b)
  if (result == 0)
    result = a <=> b
  end
  result
end
# => ["a", "buzz", "FOO", "Foo", "foo", "Z"]

–Proctor

,

Leave a Comment

Erlang Thursday – user_default

Today’s Erlang Thursday takes a look at user_default.

I stumbled across this when trying to verify what version of c/1 and l/1 was used when called in the shell, and came across shell_default documentation.

It mentioned that if there are functions that we would like to have available in the shell, we can have a module user_default and specify its location in our .erlang file in our home directory.

Since I have a fizzbuzz example in Erlang handy, lets start out with that for our user_default module as proof that we can get it working. And we’ll put it in tmp under our home directory.

-module(user_default).

-export([fizzbuzz/1]).

fizzbuzz(N) ->
    Translations = lists:map(fun translate/1, lists:seq(1, N)),
    lists:foreach(fun(Item) -> io:format("~s~n", [Item]) end, Translations).

translate(N) when N rem 3 =:= 0 andalso N rem 5 =:= 0 ->
   'FizzBuzz';
translate(N) when N rem 3 =:= 0 ->
   'Fizz';
translate(N) when N rem 5 =:= 0 ->
   'Buzz';
translate(N) ->
   integer_to_list(N).

Let’s open up a new .erlang file in our home directory, and add the following line, pointing to the location of the user_default file we just created.

code:load_abs("tmp/user_default").

If you already have a .erlang file, then the call to code:load_abs/1 for the user_default module must be the first line in the file.

Time to ensure both files are saved, and open up the erlang shell, and try calling fizzbuzz/1.

$ erl
Erlang/OTP 17 [erts-6.2.1] 1 [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V6.2.1  (abort with ^G)
1> fizzbuzz(20).
** exception error: undefined shell command fizzbuzz/1
2> q().
ok

Doesn’t seem to be working as advertised. Realizing that this is load_abs, and user_default is a new module, the error is probably because there is no file to load. Let’s go compile the module using erlc, and try again.

$ cd tmp/
$ erlc user_default.erl
$ ls user_default.*
user_default.beam user_default.erl
$ cd ..

We now have a BEAM file there, so let’s startup the Erlang shell again and try to call fizzbuzz/1.

$ erl
Erlang/OTP 17 [erts-6.2.1] 1 [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V6.2.1  (abort with ^G)
1> fizzbuzz(20).
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
ok
2>

And it works! We now have fizzbuzz/1 available for use in the shell without needing to specify a module.

For more information on the .erlang configuration file, check out the Configuration section of the Erlang Getting Started documentation.

–Proctor

,

1 Comment

Ruby Tuesday – String#rjust

Today’s Ruby Tuesday is on String#rjust.

String#rjust takes an integer, N, and returns a string of length N with the string that rjust was invoked on right aligned.

"Name".rjust 20
# => "                Name"
"Email".rjust 20
# => "               Email"
"Password".rjust 20
# => "            Password"

If the integer value passed to rjust is larger than the length of the string to right justify, the return value is a new string with the same content as the original. We can see this by comparing the object_id of the string rjust is called on with the object_id of the resulting string.

"foobar".rjust(4)
# => "foobar"
f = "foobar"
# => "foobar"
f.object_id
# => 70206438160440
f.rjust(2).object_id
# => 70206429587280
f.rjust(2).object_id
# => 70206438091960

String#rjust can also take a non-empty string as its second argument, and uses that string as the characters to pad the result with.

"Password".rjust(20, '_')
# => "____________Password"
"Password".rjust(20, '_-_')
# => "_-__-__-__-_Password"
"Password".rjust(20, ("a".."z").to_a.join)
# => "abcdefghijklPassword"
"Password".rjust(20, "")
# ArgumentError: zero width padding
# from (pry):16:in `rjust'

–Proctor

,

Leave a Comment

Erlang Thursday – c:i/0

Today’s Erlang Thursday continues with another function in the c module, c:i/0.

c:i/0 reports the system information, displaying information about all the processes on a given node.

c:i().
% Pid                   Initial Call                          Heap     Reds Msgs
% Registered            Current Function                     Stack
% <0.0.0>               otp_ring0:start/2                      987     4987    0
% init                  init:loop/1                              2
% <0.3.0>               erlang:apply/2                        6772   823443    0
% erl_prim_loader       erl_prim_loader:loop/3                   6
% <0.6.0>               gen_event:init_it/6                    376      220    0
% error_logger          gen_event:fetch_msg/5                    8
% <0.7.0>               erlang:apply/2                        1598      463    0
% application_controlle gen_server:loop/6                        7
% <0.9.0>               application_master:init/4              376       44    0
%                       application_master:main_loop/2           6
% <0.10.0>              application_master:start_it/4          233       69    0
%                       application_master:loop_it/4             5
% <0.11.0>              supervisor:kernel/1                   4185    49109    0
% kernel_sup            gen_server:loop/6                        9
% <0.12.0>              rpc:init/1                             233       35    0
% rex                   gen_server:loop/6                        9
% <0.13.0>              global:init/1                          233       51    0
% global_name_server    gen_server:loop/6                        9
% <0.14.0>              erlang:apply/2                         233       19    0
%                       global:loop_the_locker/1                 5
% <0.15.0>              erlang:apply/2                         233        3    0
%                       global:loop_the_registrar/0              2
% <0.16.0>              inet_db:init/1                         233      206    0
% inet_db               gen_server:loop/6                        9
% <0.17.0>              global_group:init/1                    233       59    0
% global_group          gen_server:loop/6                        9
% <0.18.0>              file_server:init/1                    2586     2562    0
% file_server_2         gen_server:loop/6                        9
% <0.19.0>              erlang:apply/2                        2586   155919    0
% code_server           code_server:loop/1                       3
% <0.20.0>              supervisor_bridge:standard_error/      233       41    0
% standard_error_sup    gen_server:loop/6                        9
% <0.21.0>              erlang:apply/2                         233        9    0
% standard_error        standard_error:server_loop/1             2
% <0.22.0>              supervisor_bridge:user_sup/1           610       87    0
%                       gen_server:loop/6                        9
% <0.23.0>              erlang:apply/2                         233       24    0
% user                  user:server_loop/2                       5
% <0.24.0>              kernel_config:init/1                   233      286    0
%                       gen_server:loop/6                        9
% <0.25.0>              supervisor:kernel/1                    233       58    0
% kernel_safe_sup       gen_server:loop/6                        9
% <0.29.0>              kjell_profile:init/1                   987    27100    0
% kjell_profile         gen_server:loop/6                        9
% <0.30.0>              kjell_extension:init/1                2586     3903    0
% kjell_extension       gen_server:loop/6                        9
% <0.45.0>              k_user_drv:server/2                    987     2218    0
% user_drv              k_user_drv:server_loop/5                 8
% <0.46.0>              k_group:server/3                       987    14541    0
%                       k_group:server_loop/3                    4
% <0.47.0>              erlang:apply/2                       28690     4406    0
%                       kjell:shell_rep/4                       17
% <0.48.0>              erlang:apply/2                        1598    20585    0
%                       c:pinfo/1                               49
% Total                                                      58707  1110447    0
%                                                              237
% ok

We can see that it returns the process id (pid), the initial function that started the process, the size of the heap, the number of reductions, the number of messages in the message queue, the registered name, the current function the process is in, and the stack size.

c:i/0 also includes a total of the total heap size, reductions, message queue size and stack size.

There c module also includes a c:ni/0, that reports the system information above across all nodes.

Looking at the process info, we can find a couple of processes related to kjell, which I used instead of erl for ease of finding their process information.

Looking at one of the kjell related processes, we can grab that pid, and do a deeper inspection of that process by calling c:i/3, which displays information about a process, but can reference the pid by the 3 integer values that make up the process’s pid.

c:i(0, 47, 0).
% [{current_function,{kjell,shell_rep,4}},
%  {initial_call,{erlang,apply,2}},
%  {status,waiting},
%  {message_queue_len,0},
%  {messages,[]},
%  {links,[<0.48.0>,<0.46.0>]},
%  {dictionary,[{{result,1},ok},
%               {{command,1},[{call,1,{remote,1,{atom,1,c},{atom,1,i}},[]}]},
%               {evaluator,<0.48.0>}]},
%  {trap_exit,true},
%  {error_handler,error_handler},
%  {priority,normal},
%  {group_leader,<0.46.0>},
%  {total_heap_size,46421},
%  {heap_size,28690},
%  {stack_size,17},
%  {reductions,4479},
%  {garbage_collection,[{min_bin_vheap_size,46422},
%                       {min_heap_size,233},
%                       {fullsweep_after,65535},
%                       {minor_gcs,2}]},
%  {suspending,[]}]

When looking at the information related to a specific process, we can see it shows the linked process, messages and message queue length, heap and stack information, ad various other settings that could be of use.

Again, this is not as pretty as the observer application, but they come in handy when you don’t have access past a terminal, as in working from a jump box, but it gives you a good idea of what is going on in your running Erlang node.

–Proctor

,

Leave a Comment

Ruby Tuesday – String#intern

Today’s Ruby Tuesday covers String#intern.

If you are not familiar with the concept of interning, it is when the programming language/vm uses the same reference instead of multiple copies of equal objects. This is able to be done by having immutable objects, and knowing that if two objects are the same, it is safe to represent them as the same exact thing.

In Ruby String objects are mutable, so they must be compared by using an equality operator, and they get different object ids, since they can be changed at any time.

Enter String#intern. String#intern locks down a string, and creates an immutable object that represents that string, and which reference can be re-used where ever that interned string is needed.

And the result of interning a string in Ruby? A symbol.

'foo'.intern
# => :foo
'bar'.intern
# => :bar

The benefit of using String#intern in addition to being able to create symbols from a dynamic set of shared data, such as CSV or database table headers, is that it gives you as the developer a nice way to create some complex symbols without needing to worry about the correct way of quoting a symbol as well.

'Bar'.intern
# => :Bar
'string with spaces'.intern
# => :"string with spaces"
'comic cursing: @*%$*^!!!'.intern
# => :"comic cursing: @*%$*^!!!"
'"'.intern
# => :"\""
"'".intern
# => :"'"

As we can see below, that every time we reference the string "foo", it creates a new object for that string, as seen by the different object_ids for x, y, and z. But once we intern them, even though they were different objects, they share the same interned object.

x = "foo"
=> "foo"
y = "foo"
=> "foo"
z = "foo"
=> "foo"
x.object_id
=> 70297488464940
y.object_id
=> 70297488429980
z.object_id
=> 70297488410060
x.intern.object_id
=> 592808
y.intern.object_id
=> 592808
z.intern.object_id
=> 592808

String#intern is also aliased as String#to_sym.

'foo'.to_sym
=> :foo
'bar'.to_sym
=> :bar

I wanted to highlight this as String#intern instead of String#to_sym, as having an understanding about interning is something that is useful beyond just Ruby String objects, and is applicable across multiple programming languages.

–Proctor

,

Leave a Comment

Erlang Thursday – c:regs/0

Today’s Erlang Thursday continues looking at the c module, and looks at c:regs/0.

c:regs/0 displays information about the registered processes on the current node, such as process name, process id, the number of reductions performed, and more.

c:regs().
# 
# ** Registered procs on node nonode@nohost **
# Name                  Pid          Initial Call                      Reds Msgs
# application_controlle <0.7.0>      erlang:apply/2                     463    0
# code_server           <0.19.0>     erlang:apply/2                  121202    0
# erl_prim_loader       <0.3.0>      erlang:apply/2                  156994    0
# error_logger          <0.6.0>      gen_event:init_it/6                220    0
# file_server_2         <0.18.0>     file_server:init/1                  92    0
# global_group          <0.17.0>     global_group:init/1                 59    0
# global_name_server    <0.13.0>     global:init/1                       51    0
# inet_db               <0.16.0>     inet_db:init/1                     206    0
# init                  <0.0.0>      otp_ring0:start/2                 3398    0
# kernel_safe_sup       <0.28.0>     supervisor:kernel/1                 58    0
# kernel_sup            <0.11.0>     supervisor:kernel/1              49109    0
# rex                   <0.12.0>     rpc:init/1                          35    0
# standard_error        <0.21.0>     erlang:apply/2                       9    0
# standard_error_sup    <0.20.0>     supervisor_bridge:standar           41    0
# user                  <0.24.0>     group:server/3                      36    0
# user_drv              <0.23.0>     user_drv:server/2                 1219    0
# 
# ** Registered ports on node nonode@nohost **
# Name                  Id              Command
# ok

While this is not quite as nice as what is provided by the observer GUI, this is a useful tool to be able to get an idea what what the processes are, and what they are doing when you are not able to have the observer GUI running.

The c module also contains a function c:nregs/0 which displays information about all processes for all of the nodes that the node it is run from knows about.

node().
# 'foo@127.0.0.1'
nodes().
# ['bar@127.0.0.1']
c:nregs().
# 
# ** Registered procs on node 'foo@127.0.0.1' **
# Name                  Pid          Initial Call                      Reds Msgs
# application_controlle <0.7.0>      erlang:apply/2                     463    0
# auth                  <0.19.0>     auth:init/1                        880    0
# code_server           <0.25.0>     erlang:apply/2                  122302    0
# erl_epmd              <0.18.0>     erl_epmd:init/1                    268    0
# erl_prim_loader       <0.3.0>      erlang:apply/2                  163458    0
# error_logger          <0.6.0>      gen_event:init_it/6                264    0
# file_server_2         <0.24.0>     file_server:init/1                  92    0
# global_group          <0.23.0>     global_group:init/1                 67    0
# global_name_server    <0.13.0>     global:init/1                      339    0
# inet_db               <0.16.0>     inet_db:init/1                     255    0
# init                  <0.0.0>      otp_ring0:start/2                 5405    0
# kernel_safe_sup       <0.34.0>     supervisor:kernel/1                 58    0
# kernel_sup            <0.11.0>     supervisor:kernel/1              57226    0
# net_kernel            <0.20.0>     net_kernel:init/1                  792    0
# net_sup               <0.17.0>     supervisor:erl_distributi          285    0
# rex                   <0.12.0>     rpc:init/1                          35    0
# standard_error        <0.27.0>     erlang:apply/2                       9    0
# standard_error_sup    <0.26.0>     supervisor_bridge:standar           41    0
# user                  <0.30.0>     group:server/3                      36    0
# user_drv              <0.29.0>     user_drv:server/2                 1661    0
# 
# ** Registered ports on node 'foo@127.0.0.1' **
# Name                  Id              Command
# 
# ** Registered procs on node 'bar@127.0.0.1' **
# Name                  Pid          Initial Call                      Reds Msgs
# application_controlle <6108.7.0>   erlang:apply/2                     463    0
# auth                  <6108.19.0>  auth:init/1                        880    0
# code_server           <6108.25.0>  erlang:apply/2                  124588    0
# erl_epmd              <6108.18.0>  erl_epmd:init/1                    268    0
# erl_prim_loader       <6108.3.0>   erlang:apply/2                  164400    0
# error_logger          <6108.6.0>   gen_event:init_it/6                301    0
# file_server_2         <6108.24.0>  file_server:init/1                  92    0
# global_group          <6108.23.0>  global_group:init/1                 67    0
# global_name_server    <6108.13.0>  global:init/1                      341    0
# inet_db               <6108.16.0>  inet_db:init/1                     255    0
# inet_gethost_native   <6108.42.0>  inet_gethost_native:serve           83    0
# inet_gethost_native_s <6108.41.0>  supervisor_bridge:inet_ge           41    0
# init                  <6108.0.0>   otp_ring0:start/2                 5515    0
# kernel_safe_sup       <6108.34.0>  supervisor:kernel/1                127    0
# kernel_sup            <6108.11.0>  supervisor:kernel/1              57226    0
# net_kernel            <6108.20.0>  net_kernel:init/1                  796    0
# net_sup               <6108.17.0>  supervisor:erl_distributi          285    0
# rex                   <6108.12.0>  rpc:init/1                        1302    0
# standard_error        <6108.27.0>  erlang:apply/2                       9    0
# standard_error_sup    <6108.26.0>  supervisor_bridge:standar           41    0
# user                  <6108.30.0>  group:server/3                      36    0
# user_drv              <6108.29.0>  user_drv:server/2                 2801    0
# 
# ** Registered ports on node 'bar@127.0.0.1' **
# Name                  Id              Command
# ok

–Proctor

,

Leave a Comment

Ruby Tuesday – Random::new_seed

Today’s Ruby Tuesday is on Random::new_seed.

Random::new_seed returns a new arbitrary seed value. By generating and capturing the seed value, we can have multiple instances of Random generate the same sequence of random numbers if they were constructed with the same seed.

seed = Random.new_seed
# => 90121465857858294451245401342699150799
Random.new(seed).rand(1_000_000_000)
# => 966720783
Random.new(seed).rand(1_000_000_000)
# => 966720783
Random.new(seed).rand(1_000_000_000)
# => 966720783
Random.new(seed).rand(1_000_000_000)
# => 966720783
Random.new(seed).rand(1_000_000_000)
# => 966720783

In what real world case would we care about capturing a seed?

One example where this becomes useful is creating random sets of test data, especially when one is trying to do a very, very, basic version of generative testing.

def random_list(size, seed=Random.new_seed)
  puts "seed used to generate list was: #{seed}"
  prng = Random.new(seed)
  (1..size).map{|_| prng.rand(1_000_000)}
end


random_list(10)
# seed used to generate list was: 186039884741241642189311371060927079314
# => [333029, 833700, 863953, 325452, 761340, 165891, 818711, 35680, 970562, 926764]
random_list(10)
# seed used to generate list was: 195630211850073328706621905093237636602
# => [414039, 78807, 761787, 93581, 912224, 334025, 139492, 597469, 191557, 637405]
random_list(10)
# seed used to generate list was: 305942993230783695517144566027975028636
# => [459072, 417794, 851547, 51516, 299288, 859682, 514847, 356177, 436546, 63844]

By defining a helper method like random_list above, and having it print out the seed it was using, if we use this list in a test, and that test case fails, we can reproduce the test case by getting the list generated by using the appropriate seed.

random_list(10, 195630211850073328706621905093237636602)
# seed used to generate list was: 195630211850073328706621905093237636602
# => [414039, 78807, 761787, 93581, 912224, 334025, 139492, 597469, 191557, 637405]

–Proctor

,

Leave a Comment

A List of Questions to Address Before Creating a Microservice

Last Friday, I got a meeting invite at work to discuss creating a new microservice as part of our application.

Whether it is Unix style programs, Domain Driven Design bounded contexts, classes and objects that adhere to the Single Responsibility Principle, I have been a supporter of small focused applications that have a single job to do, and do it well. This is one of the things that has appealed to me about Erlang as I have been digging ever deeper into it.

I will be first to step forward and promote the idea of microservices, but I will also be the first to come across like I don’t support them.

These questions are to make sure that proper thought is given to the implications of creating a microservice architecture, so that we don’t shoot ourselves in the foot and become the case study of why microservices are just a bunch of hot air, instead of being a case study for why and how it can work.

These questions are likely applicable to any new application, and not just microservices, and are inspired by the 8 Fallacies of Distributed Computing, the book Enterprise Integration Patterns, Domain Driven Design, my learning path with Erlang, and too many more to be named.

In no particular order at this point, but the general order at which they came into my head, here are the questions we should ask ourselves to help determine if a microservice is a good idea.

  • What other information do this service need from other parts of the system? Is this truly a vertical slice of a domain?
  • What other outside systems do we depend on for this service?
  • What happens if one of the services dependencies is unavailable?
  • How do we know if this service is running? Generating errors?
  • Which parts of the system will be consuming the service?
  • How to we abort without taking down the consumers of this service?
  • What does the size of the request look like?
  • What does the size of the response look like?
  • What is the latency of this service?
    • What is the latency of just returning a 200 OK with a hardcoded return value.
    • What is the expected latency of processing a full request?
  • What is the expected SLA of the service?
    • How do we expect to meet that SLA?
    • What is the SLA for uptime?
    • What is the SLA for response time? Average response time? 95th percentile? 99th percentile? Worst case?
  • What is our default response to return if we are about to break the SLA?
  • Are we expecting this service to be exposed to the outside world? Live within an isolated network?
  • Do we need authentication?
  • Who would be authorized to consume this service?
  • How are we expecting to manage access to this service?
  • Do we need to encrypt the data exchange?
  • What internal storage/persistance mechanism(s) do we need as part of this service to keep it isolated?
  • How many Requests per Second are we expecting this service to need to serve?
  • How do we expect this service to be deployed? What deployment dependencies are we expecting to need?
  • How frequently do we expect this service to need to be updated after deployment?
  • How many instances of this service do we think we will need to have running?
  • How do we coordinate information exchange between multiple instances of the service?
  • What is the expected time between a change notification and a consistent view of the system?
  • If any one instance of the service in a cluster fails, do the rest fail?
    • How do we keep the other instances from failing?
    • How does an instance of the service catch back up to the latest state once it has recovered?
  • If part of the service cluster fails, can we safely and automatically restart that part of the cluster?
  • How many failures in a time period do we allow before escalating a larger issue?
    • What is that time period?
    • How do we escalate issues?
  • How do we expect these larger issues to be addressed?
  • What does it take to start the service from an empty slate?
  • What does it take to stop the service?
  • Can we have multiple versions of the service deployed and serving requests at the same time?
  • How do we know what instance of the service served a request?
  • What is the strategy to resolve the service endpoint from a blank
  • What is the expected communication medium/protocol/payload we expect to be using to communicate with this service?
    • Message bus channel subscriptions? HTTP requests? REST “proper” with Hypermedia? “Dumb” REST? JSON payload? XML payload? Protobuff payloads?
  • How do we expect load to be distributed between any instances of this service?
  • When making a request to an outside service, what do we do when awaiting a response?
    • Block? Start processing another request? Do something that is not I/O based?
  • How are we expecting to manage versioning of the APIs that this service is expect to provide?
  • Does this service need to respond to incoming calls/notifications?
  • If this service does need to respond, it is expected to be synchronous, “appear” synchronous, or be completely asynchronous style of response?
  • If asynchronous responses are expected, how does the service get the information it needs to know to where to send the response to?
  • How do we expect to trace a flow between work and the requests and responses that triggered that work? Is there a way to trace causality?
  • What is the minimum infrastructure/frameworks that is needed to provide the service?
    • Is this a service? Microservice? Additional monolithic application?
  • What is the problem domain (bounded context) of this service?
    • How do we know when we are adding features that should belong in other services?
  • How many requests are we expecting are needed to complete a business use case?
    • Is there any way to shrink that number? Can requests be combined?

This is by no means a complete list of questions we should be asking ourselves, but the start of a conversation to understand the scope of what it takes for a new service to be created and deployed. These are my brain dump of questions that help a team know if they know how to swim, and how deep the water is, before diving head first into the sea of microservices.

Let me know what other questions you think are missing.

–Proctor

, , , ,

Leave a Comment

Erlang Thursday Bonus – Functional fizzbuzz

A bonus Erlang Thursday for everyone today.

This past weekend I came across the post Bro, Do You Even FizzBuzz?!? about solving FizzBuzz in Clojure without using the modulus operator.

After translating it to Ruby as a point for a co-worker and publishing that translation, I thought I would translate it to Erlang as well to see the difference.

-module(fizzbuzz).

-export([fizzbuzz/1]).

fizzbuzz(N) ->
    Results = do_fizzbuzz(N),
    lists:foreach(fun(X) -> io:format("~p~n", [X]) end, Results).

do_fizzbuzz(N) ->
    Fizzes = cycle(["", "", "fizz"], N),
    Buzzes = cycle(["", "", "", "", "buzz"], N),
    FizzBuzzes = lists:zipwith(fun lists:append/2, Fizzes, Buzzes),
    Numbers = lists:seq(1, N),
    lists:zipwith(fun translate/2, Numbers, FizzBuzzes).

cycle(List, N) ->
    lists:sublist(lists:append(lists:duplicate(N, List)), N).

translate(Number, "") ->
    integer_to_list(Number);
translate(_, Translation) ->
    Translation.

A couple of points to note in the Erlang solution.

First, Erlang doesn’t have any direct concept of lazy lists/sequences, and there is no cycle function, so I had to improvise by calling lists:duplicate, lists:append, and lists:sublist a list that cycles over the source list to create a list that has N elements. While this is not exactly the most efficient way to do this, it shows that it can be done.

Second, the ability of using lists:zipwith helps with some of the ideas of pipelining, as we can process the items as they are zipped together, instead of having to process them as a different step.

Third, instead of using a case statement, we can use a function with a guard clause to determine if a translation exists or not, and use the translation if so, and the number if not.

I hope this gives you some food for thought, and would love to hear your feedback on how this could be improved even more, or other ways that FizzBuzz can be done besides the usual pattern matching with the guard clauses checking the remainders.

–Proctor

,

4 Comments