Repository: ceph/go-ceph
Pull request #330 (2020-07-15 19:06:23)

rados: add GetLastVersion implementing rados_get_last_version
The GetLastVersion returns a number representing the most recent
read/write version for the io context.

This will become more useful later on when more write op and read op functions are implemented.

## Checklist
- [x] Added tests for features and functional changes
- [x] Public functions and types are documented
- [x] Standard formatting is applied to Go code



Patch:
diff --git a/rados/ioctx.go b/rados/ioctx.go
index d1cfd3a..598976d 100644
--- a/rados/ioctx.go
+++ b/rados/ioctx.go
@@ -92,6 +92,15 @@ type IOContext struct {
 	ioctx C.rados_ioctx_t
 }
 
+// validate returns an error if the ioctx is not ready to be used
+// with ceph C calls.
+func (ioctx *IOContext) validate() error {
+	if ioctx.ioctx == nil {
+		return ErrNotConnected
+	}
+	return nil
+}
+
 // Pointer returns a pointer reference to an internal structure.
 // This function should NOT be used outside of go-ceph itself.
 func (ioctx *IOContext) Pointer() unsafe.Pointer {
@@ -637,3 +646,16 @@ func (ioctx *IOContext) BreakLock(oid, name, client, cookie string) (int, error)
 		return int(ret), getError(ret)
 	}
 }
+
+// GetLastVersion will return the version number of the last object read or
+// written to.
+//
+// Implements:
+//  uint64_t rados_get_last_version(rados_ioctx_t io);
+func (ioctx *IOContext) GetLastVersion() (uint64, error) {
+	if err := ioctx.validate(); err != nil {
+		return 0, err
+	}
+	v := C.rados_get_last_version(ioctx.ioctx)
+	return uint64(v), nil
+}


Test patch:
diff --git a/rados/rados_test.go b/rados/rados_test.go
index 7de0ab1..7fa957e 100644
--- a/rados/rados_test.go
+++ b/rados/rados_test.go
@@ -1158,6 +1158,116 @@ func (suite *RadosTestSuite) TestOpenIOContextInvalidPool() {
 	require.Nil(suite.T(), ioctx)
 }
 
+func (suite *RadosTestSuite) TestGetLastVersion() {
+	suite.SetupConnection()
+	// NOTE: Reusing the ioctx set up by SetupConnection seems to make this
+	// test flakey. This is possibly be due to the value being "global" to the
+	// ioctx. Thus we create a new ioctx for the subtests.
+
+	suite.T().Run("write", func(t *testing.T) {
+		ioctx, err := suite.conn.OpenIOContext(suite.pool)
+		require.NoError(suite.T(), err)
+		oid := suite.GenObjectName()
+		defer suite.ioctx.Delete(oid)
+
+		v1, _ := ioctx.GetLastVersion()
+
+		err = ioctx.Write(oid, []byte("something to write"), 0)
+		assert.NoError(t, err)
+
+		v2, _ := ioctx.GetLastVersion()
+		assert.NotEqual(t, v1, v2)
+
+		v3, _ := ioctx.GetLastVersion()
+		assert.Equal(t, v2, v3)
+
+		err = ioctx.Write(oid, []byte("something completely different"), 0)
+		assert.NoError(t, err)
+
+		v4, _ := ioctx.GetLastVersion()
+		assert.NotEqual(t, v1, v4)
+		assert.NotEqual(t, v2, v4)
+		assert.NotEqual(t, v3, v4)
+	})
+
+	suite.T().Run("writeAndRead", func(t *testing.T) {
+		ioctx, err := suite.conn.OpenIOContext(suite.pool)
+		require.NoError(suite.T(), err)
+		oid := suite.GenObjectName()
+		defer ioctx.Delete(oid)
+
+		v1, _ := ioctx.GetLastVersion()
+
+		err = ioctx.Write(oid, []byte("presto"), 0)
+		assert.NoError(t, err)
+
+		v2, _ := ioctx.GetLastVersion()
+		assert.NotEqual(t, v1, v2)
+
+		bytes := make([]byte, 1024)
+		_, err = ioctx.Read(oid, bytes, 0)
+		assert.NoError(t, err)
+
+		v3, _ := ioctx.GetLastVersion()
+		assert.Equal(t, v2, v3)
+
+		err = ioctx.Write(oid, []byte("abracadabra"), 0)
+		assert.NoError(t, err)
+
+		v4, _ := ioctx.GetLastVersion()
+		assert.NotEqual(t, v1, v4)
+		assert.NotEqual(t, v2, v4)
+		assert.NotEqual(t, v3, v4)
+
+		_, err = ioctx.Read(oid, bytes, 0)
+		assert.NoError(t, err)
+
+		v5, _ := ioctx.GetLastVersion()
+		assert.Equal(t, v4, v5)
+	})
+
+	suite.T().Run("writeAndReadMultiple", func(t *testing.T) {
+		ioctx, err := suite.conn.OpenIOContext(suite.pool)
+		require.NoError(suite.T(), err)
+
+		oids := make([]string, 5)
+		vers := make([]uint64, 5)
+		for i := 0; i < 5; i++ {
+			oid := suite.GenObjectName()
+			defer ioctx.Delete(oid)
+			err = ioctx.Write(oid, []byte(oid), 0)
+			assert.NoError(t, err)
+
+			oids[i] = oid
+			vers[i], _ = ioctx.GetLastVersion()
+		}
+
+		var v uint64
+		bytes := make([]byte, 1024)
+
+		_, err = ioctx.Read(oids[0], bytes, 0)
+		assert.NoError(t, err)
+		v, _ = ioctx.GetLastVersion()
+		assert.Equal(t, vers[0], v)
+
+		_, err = ioctx.Read(oids[4], bytes, 0)
+		assert.NoError(t, err)
+		v, _ = ioctx.GetLastVersion()
+		assert.Equal(t, vers[4], v)
+
+		_, err = ioctx.Read(oids[1], bytes, 0)
+		assert.NoError(t, err)
+		v, _ = ioctx.GetLastVersion()
+		assert.Equal(t, vers[1], v)
+	})
+
+	suite.T().Run("invalidIOContext", func(t *testing.T) {
+		ioctx := &IOContext{}
+		_, err := ioctx.GetLastVersion()
+		assert.Error(t, err)
+	})
+}
+
 func TestRadosTestSuite(t *testing.T) {
 	suite.Run(t, new(RadosTestSuite))
 }