Subject: updated minor versioning spec

From: Mike Eisler <[email-masked]>
Date: Tue, 16 Nov 1999 05:14:22 -0800 (PST)

> I'll follow up this e-mail with the re-worked minor versioning document
> that synthesizes all the discussion on the proposal and my proposed
> changes to it.

Here it is. The attched "minor.diffs" file shows the context differences
from the proposal of two weeks ago versus today's update.

rules for minor versioning
--------------------------

A. To make life easier for customers, the protocol should encode a minor
version # that corresponds to a published RFC.

	There have been objections to adding minor version number
	encoding overhead to the protocol. In my experience, customers
	simple hate it when they get a product that is versionable, but
	there are no tools to determine the version. It makes support a
	nightmare if you can't easily establish over a telephone
	conversation what version the customer has. If anything, this
	issue is more important to nail down with network products than
	stand alone products.

B. Minor versions do not add/delete procedures

	There was early objection on the alias around
	a minor versioning proposal that did so. The objections
	can be summarized as:

		1. RPC already has a mechanism for adding/deleting
		procedures; change the version field in the call header
		and then change the set of procedures.

		2. PROCUNVAIL error handling is more complex when
		NFS V4 can add or delete procedures.

		3. Perhaps NFSv4 should be a suite of multiple
		program numbers, each of which can be versioned
		using the normal RPC convention via the program's
		version field in the call header.

	One and two taken together simply mean that we shouldn't
	add RPC procedures in an existing program/version pair,
	since it is rather unorthodox.

	Point number three was never supported by a concrete
	proposal. Since then the NFSv4 protocol has collapsed to
	just two procedures, NULL and COMPOUND. Since everything
	is done with COMPOUND, adding/deleting procedures is not
	an issue.

C. Minor versions can add operations to COMPOUND.

	Since everything is done under COMPOUND, there is no impact on
	an implementation's RPC framework. There's nothing unorthodox
	about doing so ... it's merely a switched union. A precedent
	is how well the RPC framework copes with adding security
	flavors to via the RPC header's AUTH credential and verifier
	fields.

C.1 Minor versions can append attributes to GETATTR4args, bitmap4,
and GETATTR4res.

	Naturally. However ...

C.2 Minor version X must append any new attributes after the last
documented attribute.

	Suppose we add a mandatory attribute and stick it in the
	first bitmap word. Given the way attribute results are
	specified, as an opaque array of per-attribute XDR encoded
	results, packet sniffers for minor version X-1 will have an
	impossible time parsing the bit stream. We should not
	negatively impact the ability to diagnose problems.

D. Minor versions cannot modify the structure of an existing
	operation's arguments and results.

	It's going to get messy to have to deal with N different
	variations of something like OPEN, which is already quite
	complex. Note that this rule doesn't preclude:

		- adding bits to flag fields, such as new attributes to
		GETATTR's bitmap4 data type.

		- adding bits to existing attributes like ACLs that have flag
		words.

		- extending enumerated types (including NFS4ERR_*)
			with new values.

	The point is to preserve backward binary compatibility on the wire.

	The side benefit is that a packet sniffer written for minor
	version X can do a reasonable job of parsing operations for
	minor version X+1, albeit with a warning to the user that
	not everything can be parsed.

D.1. Minor versions cannot modify the structure of an existing
	attribute.

E. Minor versions cannot delete operations.

	Think it through and you find this is a corollary to
	rule D above. If you can delete an operation in minor version X, then
	what's to prevent version X+1 from introducing a new operation
	in the same slot with same opcode value? Of course, you
	could have a rule that states that if an opcode is reused,
	it has to use a compatible struct, but that is error prone.

E.1 Minor versions cannot delete attributes.

E.2 Minor versions cannot delete flag bits or enumeration values.

F. Minor versions can declare an operation as mandatory to not implement.

	Suppose in minor version X operation Y is declared "mandatory
	to not implement" or obsolete. This means a client should not
	send it, and a server should return an NFS error as a opposed
	to dropping the request as an XDR decode error.

	This is a way to delete the *support* of an operation, but
	still preserve its structure definition so that future minor
	versions can unobsolete the procedure if necessary.

	There's precedence for this. Recall the WRITECACHE procedure of
	NFS Version 2. It's there in the specification, and implementors 
	are told that they shouldn't implement it.
	
F.1 Minor versions can declare attributes mandatory to not implement.

F.2 Minor versions can declare flag bits or enumeration values
	as mandatory to not implement.

G. Minor versions can downgrade features from mandatory to recommended,
	or recommended to optional.

H. Minor versions can upgrade features from optional to recommended or
	recommended to mandatory.

I. A client and server that supports minor version X must support
minor versions 0 through X-1 as well.

	Otherwise, it's an interoperability nightmare. The counter argument,
	what if one particular minor version is so bad that one would
	rather not support it? First, since the minor versions have to
	got through the IETF process, one would hope that due diligence
	would avoid this. Second, if version X is so bad, if if version X-1
	was considered pretty good, then it's not rocket science to publish
	a version X+1 that is a clone of X-1. Business considerations will
	encourage implementation X+1.

J. No new features can be introduced as mandatory in a minor version.

	Suppose in minor version X we introduced a new operation, "find
	recursive" which given a directory file handle, returns the
	directory entries for the directory, and its subdirectories
	recursively. Implementors would quickly find that this operation
	is a bad idea, and the next minor version, X+1 would declare it
	as mandatory to not implement. However, if "find recursive"
	were mandatory to implement in minor version X then rule I
	would still force servers to implement it. By requiring that
	new features be introduced as non-mandatory, it gives us
	implementation experience.

K. A client MUST NOT attempt to use a stateid, file handle, or similar
returned object from a COMPOUND with version X for another COMPOUND
with version Y, where X != Y.

proposal
--------

I. Add a minor version field to COMPOUND4args. Initially supported value
is 0.

II. Add an NFS4ERR_MINOR_VERS_MISMATCH error status.

	When the server gets an unsupported minor version, it MUST
	return a COMPOUND4res with a zero length operations result
	array

III. Reserve operation number 2 as an operation we can use for version
negotiation on the day when we actually have more than 1 minor
version.

	Why not solve that issue today?
		Because we don't have to and have some real work to do
		to get minor version 0 out the door.

		Because if we attempt to we will probably get it
		wrong.  Since we can't predict how frequently we'll
		make minor revisions, nor how much delta each change
		will be, it seems likely we'll either over engineer or
		under engineer.

	What might the operation look like?

		In the future, operation 2 might be as simple as one
		with void arguments in the operation, and the highest
		supported version in the COMPOUND4args. The server
		returns an NFS4_OK if supported,
		NFS4ERR_MINOR_VERS_MISMATCH, if not.

		Or it might be as complex as the client sending an
		array structs, each element representing a minor
		version. Each element would contain a bit mask of the
		client's supported operations, which the server would
		logically AND with its supported operations.

		The operation itself might have its own version number
		independent of the NFS V4 minor version field to allow
		us to develop more flexible schemes.

		More than likely it will be something between the
		extremes.

	How does a NFSv4 client that supports minor versions > 0
	detect that the server supports only version 0?

		We do have to specify this aspect of version
		negotiation protocol now.

		When the client issues a COMPOUND request with say
		minor version 5 in the COMPOUND4args, the server
		returns a zero-length operations result with the error
		NFS4ERR_MINOR_VERS_MISMATCH. The client has two
		choices:

			1. re-issue the COMPOUND with a lower minor
			version.

			2. issue a COMPOUND with opcode 2, and the
			minor version field set to 1 or higher.

		In either case the server will return
		NFS4ERR_MINOR_VERS_MISMATCH.  If the minor version
		number in the compound arguments that generated the
		NFS4ERR_MINOR_VERS_MISMATCH was 1, then the client
		knows definitively that the server supports just minor
		version 0.

	Once we add minor versions, won't clients have to "guess",
	possibly several times?

		"Today" all clients support minor version 0. Thus those
		clients will never need to guess, because they never
		get a mismatch error, because V4 servers will always
		need to support minor version 0.

		"Tomorrow" there might be a client that supports
		version 10. It will first issue a COMPOUND with minor
		version set to 1, and opcode set to 2. The result will
		either be a version mismatch, meaning that the server
		supports just minor version 0, or a success, with
		sufficient information to indicate what version the
		server does support.

IV. In minor version 0 of the specification, state that as part of the
	process of producing the first minor version, any of the aforementioned
	rules might be re-visited.
*** minor.orig	Tue Nov 16 04:25:58 1999
--- minor-vers	Tue Nov 16 05:04:19 1999
***************
*** 66,80 ****
  	negatively impact the ability to diagnose problems.
  
  D. Minor versions cannot modify the structure of an existing
! 	operation.
  
  	It's going to get messy to have to deal with N different
  	variations of something like OPEN, which is already quite
! 	complex. Note that this rule doesn't mean we can't add bits to
! 	flag fields, such as new attributes to GETATTR's bitmap4 data
! 	type.  The point is to preserve backward binary compatibility on
! 	the wire.
  
  	The side benefit is that a packet sniffer written for minor
  	version X can do a reasonable job of parsing operations for
  	minor version X+1, albeit with a warning to the user that
--- 66,88 ----
  	negatively impact the ability to diagnose problems.
  
  D. Minor versions cannot modify the structure of an existing
! 	operation's arguments and results.
  
  	It's going to get messy to have to deal with N different
  	variations of something like OPEN, which is already quite
! 	complex. Note that this rule doesn't preclude:
  
+ 		- adding bits to flag fields, such as new attributes to
+ 		GETATTR's bitmap4 data type.
+ 
+ 		- adding bits to existing attributes like ACLs that have flag
+ 		words.
+ 
+ 		- extending enumerated types (including NFS4ERR_*)
+ 			with new values.
+ 
+ 	The point is to preserve backward binary compatibility on the wire.
+ 
  	The side benefit is that a packet sniffer written for minor
  	version X can do a reasonable job of parsing operations for
  	minor version X+1, albeit with a warning to the user that
***************
*** 92,110 ****
  	could have a rule that states that if an opcode is reused,
  	it has to use a compatible struct, but that is error prone.
  
- 	What about deleting flag bits? In principle, except for
- 	oddities like bitmap4, this might be all right. However before
- 	the WG decides to do so in a particular minor version, it
- 	should consider the implications.
- 
  E.1 Minor versions cannot delete attributes.
  
  F. Minor versions can declare an operation as mandatory to not implement.
  
! 	Suppose in minor version X operation Y is declared obsolete. This
! 	means a client should not send it, and a server should return
! 	an NFS error as a opposed to dropping the request as an XDR
! 	decode error.
  
  	This is a way to delete the *support* of an operation, but
  	still preserve its structure definition so that future minor
--- 100,115 ----
  	could have a rule that states that if an opcode is reused,
  	it has to use a compatible struct, but that is error prone.
  
  E.1 Minor versions cannot delete attributes.
  
+ E.2 Minor versions cannot delete flag bits or enumeration values.
+ 
  F. Minor versions can declare an operation as mandatory to not implement.
  
! 	Suppose in minor version X operation Y is declared "mandatory
! 	to not implement" or obsolete. This means a client should not
! 	send it, and a server should return an NFS error as a opposed
! 	to dropping the request as an XDR decode error.
  
  	This is a way to delete the *support* of an operation, but
  	still preserve its structure definition so that future minor
***************
*** 116,122 ****
  	
  F.1 Minor versions can declare attributes mandatory to not implement.
  
! G. A client and server that supports minor version X must support
  minor versions 0 through X-1 as well.
  
  	Otherwise, it's an interoperability nightmare. The counter argument,
--- 121,136 ----
  	
  F.1 Minor versions can declare attributes mandatory to not implement.
  
! F.2 Minor versions can declare flag bits or enumeration values
! 	as mandatory to not implement.
! 
! G. Minor versions can downgrade features from mandatory to recommended,
! 	or recommended to optional.
! 
! H. Minor versions can upgrade features from optional to recommended or
! 	recommended to mandatory.
! 
! I. A client and server that supports minor version X must support
  minor versions 0 through X-1 as well.
  
  	Otherwise, it's an interoperability nightmare. The counter argument,
***************
*** 128,134 ****
  	a version X+1 that is a clone of X-1. Business considerations will
  	encourage implementation X+1.
  
! H. A client MUST NOT attempt to use a stateid, file handle, or similar
  returned object from a COMPOUND with version X for another COMPOUND
  with version Y, where X != Y.
  
--- 142,161 ----
  	a version X+1 that is a clone of X-1. Business considerations will
  	encourage implementation X+1.
  
! J. No new features can be introduced as mandatory in a minor version.
! 
! 	Suppose in minor version X we introduced a new operation, "find
! 	recursive" which given a directory file handle, returns the
! 	directory entries for the directory, and its subdirectories
! 	recursively. Implementors would quickly find that this operation
! 	is a bad idea, and the next minor version, X+1 would declare it
! 	as mandatory to not implement. However, if "find recursive"
! 	were mandatory to implement in minor version X then rule I
! 	would still force servers to implement it. By requiring that
! 	new features be introduced as non-mandatory, it gives us
! 	implementation experience.
! 
! K. A client MUST NOT attempt to use a stateid, file handle, or similar
  returned object from a COMPOUND with version X for another COMPOUND
  with version Y, where X != Y.
  
***************
*** 219,221 ****
--- 246,252 ----
  		supports just minor version 0, or a success, with
  		sufficient information to indicate what version the
  		server does support.
+ 
+ IV. In minor version 0 of the specification, state that as part of the
+ 	process of producing the first minor version, any of the aforementioned
+ 	rules might be re-visited.