Contents:
About this document
Introduction to Transis
The Transis System
Group Communication
Virtual and Extended Virtual Synchrony
Layers Over Transis
Examples, Tricks, and Pitfalls
Transis: The Transis User Tutorial Programmer's Manual by Dalia Malki.
This Is Root
This document is a beginner's course on the Transis environment. It supplements The Transis User Tutorial by Dalia Malki. It will therefore not explain the syntax of commands, but more about their semantics. It will also give some typical examples on how to use the Transis approach in distributed applications, and describe several pitfalls that can be avoided.
This document was submitted as a lab project in the High Availability Lab, Computer Science Department, The Hebrew University, Jerusalem, Israel.
Acknowledgments to all the lab
members and other faithful readers who contributed ideas, pointed out
vague issues, and spent time reading this document and commenting on it.
If you have any comments or questions, please
mail.
Introduction to Transis
Transis is a multicast communication layer that facilitates the development of
fault tolerant applications in a network of machines.
It supports process group communication:
A process group is a group of processes conveniently identified by a name
(a string) selected by the user.
Messages addressed to the group are received by all group members (under
specific constraints). Thus, the sender is not required to from identify all
the targets of the message explicitly, or find the network route to them.
Transis also supports a membership service:
If processes are added to a group or deleted from it (due to process
crash, changes in the network or the user's preference), Transis will report
the change to all active group members, while keeping consistency among them
(more about consistency in Virtual and Extended Virtual
Synchrony).
To understand what group communication is, consider the following example. Imagine that you and your friends are going on a picnic, and each person has to bring something from the following list - drinks, salads, bread and rolls, cake, dishes, silver ware, and so forth. The problem is that you don't have time to meet and discuss things, so you must use some communication mode - probably the telephone.
This would usually take endless phone calls in which only two persons can communicate at once. Information like "is anyone bringing a cake" must be exchanged and repeated in many calls - quite awkward. Transis, on the other hand, is similar to a conference call. The group communication facility allows you and your friends to form the "conference." Calling this new entity will make the phone ring at all your friends' homes, and after they all pick up their phones, everyone will be able to hear everyone and speak to everyone as if you were all in the same room.
Using TCP for group communication is similar to using simple phone calls in the above example - it is possible but rather complicated: A socket should be installed between each two processes, and messages would have to be repeated over various channels in order to inform everyone.
Transis, in contrast, takes advantage of the multicast facility available in current networks. Thus, much like in the above example, a message is only sent once. Moreover all-or-none delivery semantics are guaranteed, message losses and transient network failures are transparent, and message ordering is supplied.
In TCP, membership changes (such as when connection between two computers fails) must be managed by the user, through a timeout mechanism of some sort. Hence user tasks become much more complicated. Transis, on the other hand, reports membership changes, while guaranteeing additional information.
an application example - user.c
The following is a very simple application written over Transis, that
demonstrates Transis facilities.
(It can be found in /cs/transis/src/ring/user.c.)
When zzz_user.c is activated, the process contacts Transis: It creates for itself a singleton group, and receives a `mailbox' to which messages will arrive. Then you can do the following:
A more detailed explanation on the different facilities may be found in Transis: The Transis User Tutorial
#include stdio.h #include "zzz_layer.h" #include "sim_layer.h" #define MAX_MEMBERS 100 # define USAGE "Usage : %s [-sim] [-simname] [-sig] [-name ] [-group_service <0/1>] \n", argv[0] #define TEXT "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzzaabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz" static zzz_mbox_cap my_mbox; static mbox_cap ha_mbox; static char send_buf[40000]; static char recv_buf[40000]; static int num_send = 0; static int async=0; void Print(void); void Handle_user(int, void *); void Handle_mess(int, void *); main(int argc, char **argv) { int sim_requested = 0; int sig_requested = 0; int flag = 1; char *stack = NULL; /* default layer stack will be used */ char **parms; char *my_name = NULL; char *sim_name = NULL; /* If "-sim" was specified the stack of layers will * contain the SIM layer before the default layer. */ parms = argv+1; while (argc > 1) { if (!strcmp("-sim", parms[0])) { argc--; parms++; sim_requested = 1; } else if (!strcmp("-sig", parms[0])) { argc--; parms++; sig_requested = 1; } else if (!strcmp("-name", parms[0])){ argc--; parms++; if(argc == 1){ printf(USAGE); exit(1); } my_name = parms[0]; parms++; argc--; } else if (!strcmp("-simname", parms[0])){ argc--; parms++; if(argc == 1){ printf(USAGE); exit(1); } sim_name = parms[0]; parms++; argc--; } else if (!strcmp("-group_service", parms[0])){ argc--; parms++; if(argc == 1){ printf(USAGE); exit(1); } flag = atoi(parms[0]); parms++; argc--; } else break; } if (sim_requested || sig_requested) { /* Concatinate "SIM:" and/or "SIG:" * with the default layer stack. */ stack = (char *) malloc (strlen("SIM:")*sim_requested + strlen("SIG:")*sig_requested + strlen(zzz_DEFAULT_LAYERS) + 1); if (stack == NULL) error (1, 0, "memory problem"); if (sim_requested && sig_requested) strcpy(stack, "SIG:SIM:"); else if (sim_requested) strcpy(stack, "SIM:"); else strcpy(stack, "SIG:"); strcat(stack, zzz_DEFAULT_LAYERS); } if (argc > 1) { printf(USAGE); exit(1); } if((sim_name) && (sim_requested)){ SIM_Set_Name(NULL, sim_name); } if(!my_name) my_name = argv[0]; my_mbox = zzz_Connect(my_name, stack, flag); if (my_mbox == NULL) error(1,0,"connect failed"); ha_mbox = zzz_Focus (my_mbox, "HA"); strcpy(send_buf, "hello"); send_buf[3000] = 'a'; E_init(); E_add_sock( (long)0, Handle_user, USER_PRIORITY, NULL); Print(); printf("\nenter command: "); fflush(stdout); E_main_loop(); } void Handle_user(int dummy1, void *dummy2) { char buf[80]; char command[80]; char *name; int num_args; int l,i; int recv_type ; view *gview; if (gets(buf) == NULL) exit(0); name = (char *) malloc (80) ; num_args = sscanf(buf,"%s%s", command, name ); if (name[0]=='@') name = TEXT; if (num_args == 1) { if (!strcmp(command,"r")) { printf("attempt receive (blocking)\n"); Handle_mess(-1, 0); } else if (!strcmp(command,"p")) { printf("num bytes: %d\n", zzz_Poll(my_mbox) ); } else if (!strcmp(command,"q")) { printf("Have a nice night\n"); exit(0); } else if (!strcmp(command,"a")) { if (async) { zzz_Remove_Upcall(my_mbox); async = 0; } else { zzz_Add_Upcall(my_mbox, Handle_mess, USER_PRIORITY, (void *) 1); async = 1; } } else if (!strcmp(command,"g")) { HA_Set_Group_Service(ha_mbox); printf("setting group service\n"); } else if (!strcmp(command,"u")) { HA_Reset_Group_Service(ha_mbox); printf("reseting group service\n") ; } else { printf("Unrecognized command \n"); Print(); } } else if (num_args == 2 ) { if (!strcmp(command,"j") ) { zzz_Join(my_mbox, name); } else if (!strcmp(command,"l") ) { zzz_Leave(my_mbox, name); } else if (!strcmp(command,"s") ) { sprintf(send_buf,"mess num %5d ",++num_send); l = zzz_VaSend(my_mbox, CAUSAL, 0, 3500, send_buf, name, NULL); printf("send %d bytes\n", l); } else if (!strcmp(command,"b") ) { for(i=0; i<10; i++) { sprintf(send_buf,"mess num %5d ",++num_send); l = zzz_VaSend(my_mbox, CAUSAL, 0, 1000, send_buf, name, NULL); printf("send %d bytes\n", l); } } else { printf("Unrecognized command \n"); Print(); } } printf("\nenter command: "); fflush(stdout); } void Handle_mess(int dummy1, void *param) { int if_async = (int) param; /* 1 if called asynchronously */ int i, recv_type ; view *gview; printf("\n-----------\n"); printf("Handle_mess:"); zzz_Receive(my_mbox, recv_buf, 40000, &recv_type, &gview); printf("message type is %d\n",recv_type) ; if( recv_type != VIEW_CHANGE) printf("receive handler got mess %s\n", recv_buf); else { printf("group is %s\n", gview->members[0]) ; printf("no. of clients is %d\n", gview->nmembers); for( i=1 ; i<= gview->nmembers ; i++ ) printf("%s ", gview->members[i]) ; printf("\n") ; } if (if_async) { printf("\nenter command: "); fflush(stdout); } } void Print() { printf("User commands : \n"); printf(" j -- join\n"); printf(" l -- leave\n"); printf(" s -- send\n"); printf(" b -- send burst of 10 msgs\n"); printf(" r -- receive\n"); printf(" p -- poll\n"); printf(" a -- toggle async mode\n"); printf(" g -- set group service\n") ; printf(" u -- reset group service\n") ; printf(" q -- quit\n"); }