Skip to content

Commit

Permalink
Update documentation about fjson package (#262)
Browse files Browse the repository at this point in the history
Closes #182.
  • Loading branch information
AlekSi committed Jan 15, 2022
1 parent e4e9e24 commit 8e6d03b
Show file tree
Hide file tree
Showing 18 changed files with 104 additions and 56 deletions.
9 changes: 9 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ run:
issues-exit-code: 0

linters-settings:
depguard:
list-type: blacklist
packages:
- github.com/FerretDB/FerretDB/internal/bson
exhaustive:
default-signifies-exhaustive: false
goconst:
Expand Down Expand Up @@ -120,3 +124,8 @@ linters:

issues:
exclude-use-default: false
exclude-rules:
# only `wire` package can import `bson` package
- linters: [depguard]
path: internal/wire
text: bson
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,22 @@ $ git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/FerretDB.git
This allows you to run commands against FerretDB.

You can see all available "make" commands with `make help`.

## Code Overview

Package `cmd` provides commands implementation. `ferretdb` is the main FerretDB binary; others are tools for development.

Package `tools` uses "tools.go" approach to fix tools versions. They are installed into `bin/` by `make init`.

`internal` subpackages contain most of the FerretDB code:
* `types` package provides Go types matching BSON types that don't have built-in Go equivalents: we use `int32` for BSON's int32, but types.ObjectID for BSON's ObjectId.
* `fjson` provides converters from/to FJSON for built-in and `types` types.
FJSON adds some extensions to JSON for keeping object keys in order, preserving BSON type information, etc.
FJSON is used by `jsonb1` handler/storage.
* `bson` package provides converters from/to BSON for built-in and `types` types.
* `wire` package provides wire protocol implementation.
* `clientconn` package provides client connection implementation.
It accepts client connections, reads `wire`/`bson` protocol messages, and passes them to `handlers`.
Responses are then converted to `wire`/`bson` messages and sent back to the client.
* `handlers` handle protocol commands.
They use `fjson` package for storing data in PostgreSQL in jsonb columns, but they don't use `bson` package – all data is represented as built-in and `types` types.
10 changes: 2 additions & 8 deletions internal/bson/bson.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package bson provides converters from/to BSON.
// Package bson provides converters from/to BSON for built-in and `types` types.
//
// All BSON data types have three representations in FerretDB:
//
// 1. As they are used in handlers implementation (types package).
// 2. As they are used in the wire protocol implementation (bson package).
// 3. As they are used to store data in PostgreSQL (fjson package).
//
// The reason for that is a separation of concerns: to avoid method names clashes, to simplify type asserts, etc.
// See contributing guidelines and documentation for package `types` for details.
package bson

import (
Expand Down
3 changes: 2 additions & 1 deletion internal/bson/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import (
)

const (
MaxDocumentLen = 16777216
// Deprecated: use types.MaxDocumentLen instead.
MaxDocumentLen = types.MaxDocumentLen

minDocumentLen = 5
)
Expand Down
1 change: 1 addition & 0 deletions internal/clientconn/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package clientconn provides client connection implementation.
package clientconn

import (
Expand Down
46 changes: 20 additions & 26 deletions internal/fjson/fjson.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package fjson provides converters from/to FJSON.
// Package fjson provides converters from/to FJSON (JSON with some extensions) for built-in and `types` types.
//
// All BSON data types have three representations in FerretDB:
// See contributing guidelines and documentation for package `types` for details.
//
// 1. As they are used in handlers implementation (types package).
// 2. As they are used in the wire protocol implementation (bson package).
// 3. As they are used to store data in PostgreSQL (fjson package).
// Mapping
//
// The reason for that is a separation of concerns: to avoid method names clashes, to simplify type asserts, etc.
//
// JSON mapping for storage
//
// Composite/pointer types
// Document: {"$k": ["<key 1>", "<key 2>", ...], "<key 1>": <value 1>, "<key 2>": <value 2>, ...}
// Array: JSON array
// Scalar/value types
// Double: {"$f": JSON number} or {"$f": "Infinity|-Infinity|NaN"}
// String: JSON string
// Binary: {"$b": "<base 64 string>", "s": <subtype number>}
// ObjectID: {"$o": "<ObjectID as 24 character hex string"}
// Bool: JSON true / false values
// DateTime: {"$d": milliseconds since epoch as JSON number}
// nil: JSON null
// Regex: {"$r": "<string without terminating 0x0>", "o": "<string without terminating 0x0>"}
// Int32: JSON number
// Timestamp: {"$t": "<number as string>"}
// Int64: {"$l": "<number as string>"}
// Decimal128: {"$n": "<number as string>"}
// CString: {"$c": "<string without terminating 0x0>"}
// Composite types
// types.Document {"$k": ["<key 1>", "<key 2>", ...], "<key 1>": <value 1>, "<key 2>": <value 2>, ...}
// *types.Array JSON array
// Scalar types
// float64 {"$f": JSON number} or {"$f": "Infinity|-Infinity|NaN"}
// string JSON string
// types.Binary {"$b": "<base 64 string>", "s": <subtype number>}
// types.ObjectID {"$o": "<ObjectID as 24 character hex string"}
// bool JSON true / false values
// time.Time {"$d": milliseconds since epoch as JSON number}
// nil JSON null
// types.Regex {"$r": "<string without terminating 0x0>", "o": "<string without terminating 0x0>"}
// int32 JSON number
// types.Timestamp {"$t": "<number as string>"}
// int64 {"$l": "<number as string>"}
// TODO Decimal128 {"$n": "<number as string>"}
// types.CString {"$c": "<string without terminating 0x0>"}
package fjson

import (
Expand Down
7 changes: 3 additions & 4 deletions internal/handlers/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"

"github.com/FerretDB/FerretDB/internal/bson"
"github.com/FerretDB/FerretDB/internal/handlers/jsonb1"
"github.com/FerretDB/FerretDB/internal/handlers/sql"
"github.com/FerretDB/FerretDB/internal/pg"
Expand Down Expand Up @@ -433,7 +432,7 @@ func TestReadOnlyHandlers(t *testing.T) {
"versionArray", types.MustNewArray(int32(5), int32(0), int32(42), int32(0)),
"bits", int32(strconv.IntSize),
"debug", version.Get().Debug,
"maxBsonObjectSize", int32(bson.MaxDocumentLen),
"maxBsonObjectSize", int32(16777216),
"ok", float64(1),
"buildEnvironment", types.MustMakeDocument(),
),
Expand Down Expand Up @@ -687,7 +686,7 @@ func TestReadOnlyHandlers(t *testing.T) {
resp: types.MustMakeDocument(
"helloOk", true,
"ismaster", true,
"maxBsonObjectSize", int32(bson.MaxDocumentLen),
"maxBsonObjectSize", int32(16777216),
"maxMessageSizeBytes", int32(wire.MaxMsgLen),
"maxWriteBatchSize", int32(100000),
"localTime", time.Now(),
Expand All @@ -708,7 +707,7 @@ func TestReadOnlyHandlers(t *testing.T) {
resp: types.MustMakeDocument(
"helloOk", true,
"ismaster", true,
"maxBsonObjectSize", int32(bson.MaxDocumentLen),
"maxBsonObjectSize", int32(16777216),
"maxMessageSizeBytes", int32(wire.MaxMsgLen),
"maxWriteBatchSize", int32(100000),
"localTime", time.Now(),
Expand Down
6 changes: 3 additions & 3 deletions internal/handlers/msg_buildinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (
"context"
"strconv"

"github.com/FerretDB/FerretDB/internal/bson"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/util/version"
"github.com/FerretDB/FerretDB/internal/wire"
)
Expand All @@ -35,10 +35,10 @@ func (h *Handler) MsgBuildInfo(ctx context.Context, msg *wire.OpMsg) (*wire.OpMs
Documents: []types.Document{types.MustMakeDocument(
"version", versionValue,
"gitVersion", version.Get().Commit,
"versionArray", types.MustNewArray(int32(5), int32(0), int32(42), int32(0)),
"versionArray", must.NotFail(types.NewArray(int32(5), int32(0), int32(42), int32(0))),
"bits", int32(strconv.IntSize),
"debug", version.Get().Debug,
"maxBsonObjectSize", int32(bson.MaxDocumentLen),
"maxBsonObjectSize", int32(types.MaxDocumentLen),
"ok", float64(1),
"buildEnvironment", version.Get().BuildEnvironment,
)},
Expand Down
3 changes: 2 additions & 1 deletion internal/handlers/msg_getcmdlineopts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/util/must"
"github.com/FerretDB/FerretDB/internal/wire"
)

Expand All @@ -27,7 +28,7 @@ func (h *Handler) MsgGetCmdLineOpts(ctx context.Context, msg *wire.OpMsg) (*wire
var reply wire.OpMsg
err := reply.SetSections(wire.OpMsgSection{
Documents: []types.Document{types.MustMakeDocument(
"argv", types.MustNewArray("ferretdb"),
"argv", must.NotFail(types.NewArray("ferretdb")),
"parsed", types.MustMakeDocument(),
"ok", float64(1),
)},
Expand Down
3 changes: 1 addition & 2 deletions internal/handlers/msg_hello.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"context"
"time"

"github.com/FerretDB/FerretDB/internal/bson"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/internal/wire"
Expand All @@ -33,7 +32,7 @@ func (h *Handler) MsgHello(ctx context.Context, msg *wire.OpMsg) (*wire.OpMsg, e
"helloOk", true,
"ismaster", true,
// topologyVersion
"maxBsonObjectSize", int32(bson.MaxDocumentLen),
"maxBsonObjectSize", int32(types.MaxDocumentLen),
"maxMessageSizeBytes", int32(wire.MaxMsgLen),
"maxWriteBatchSize", int32(100000),
"localTime", time.Now(),
Expand Down
3 changes: 1 addition & 2 deletions internal/handlers/query_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"context"
"time"

"github.com/FerretDB/FerretDB/internal/bson"
"github.com/FerretDB/FerretDB/internal/handlers/common"
"github.com/FerretDB/FerretDB/internal/types"
"github.com/FerretDB/FerretDB/internal/wire"
Expand All @@ -35,7 +34,7 @@ func (h *Handler) QueryCmd(ctx context.Context, query *wire.OpQuery) (*wire.OpRe
"helloOk", true,
"ismaster", true,
// topologyVersion
"maxBsonObjectSize", int32(bson.MaxDocumentLen),
"maxBsonObjectSize", int32(types.MaxDocumentLen),
"maxMessageSizeBytes", int32(wire.MaxMsgLen),
"maxWriteBatchSize", int32(100000),
"localTime", time.Now(),
Expand Down
2 changes: 2 additions & 0 deletions internal/types/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ func NewArray(values ...any) (*Array, error) {
}

// MustNewArray is a NewArray that panics in case of error.
//
// Deprecated: use `must.NotFail(NewArray(...))` instead.
func MustNewArray(values ...any) *Array {
a, err := NewArray(values...)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions internal/types/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package types

//go:generate ../../bin/stringer -linecomment -type BinarySubtype

// BinarySubtype represents BSON Binary's subtype.
type BinarySubtype byte

const (
Expand All @@ -29,6 +30,7 @@ const (
BinaryUser = BinarySubtype(0x80) // user
)

// Binary represents BSON type Binary.
type Binary struct {
Subtype BinarySubtype
B []byte
Expand Down
24 changes: 15 additions & 9 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Package types provides Go types matching BSON types without built-in Go equivalents.
// Package types provides Go types matching BSON types that don't have built-in Go equivalents.
//
// All BSON data types have three representations in FerretDB:
//
// 1. As they are used in handlers implementation (types package).
// 2. As they are used in the wire protocol implementation (bson package).
// 3. As they are used to store data in PostgreSQL (fjson package).
// 1. As they are used in "business logic" / handlers - `types` package.
// 2. As they are used in the wire protocol implementation - `bson` package.
// 3. As they are used to store data in PostgreSQL - `fjson` package.
//
// The reason for that is a separation of concerns: to avoid method names clashes, to simplify type asserts, etc.
// The reason for that is a separation of concerns: to avoid method names clashes, to simplify type asserts,
// to make refactorings and optimizations easier, etc.
//
// Mapping
//
// Composite/pointer types
// Composite types
// types.Document *bson.Document *fjson.Document
// *types.Array *bson.Array *fjson.Array
// Scalar/value types
// Scalar types
// float64 *bson.Double *fjson.Double
// string *bson.String *fjson.String
// types.Binary *bson.Binary *fjson.Binary
Expand All @@ -39,7 +40,6 @@
// int32 *bson.Int32 *fjson.Int32
// types.Timestamp *bson.Timestamp *fjson.Timestamp
// int64 *bson.Int64 *fjson.Int64
// TODO Decimal128
// types.CString *bson.CString *fjson.CString
package types

Expand All @@ -48,7 +48,9 @@ import (
"time"
)

// CompositeType represents composite/pointer type - Document or *Array.
const MaxDocumentLen = 16777216

// CompositeType represents composite type - Document or *Array.
type CompositeType interface {
Document | *Array
GetByPath(path ...string) (any, error)
Expand All @@ -59,15 +61,19 @@ type CompositeType interface {
//go-sumtype:decl CompositeType

type (
// CString represents BSON type CString that used as document field name, etc.
CString string

// ObjectID represents BSON type ObjectID.
ObjectID [12]byte

// Regex represents BSON type Regex.
Regex struct {
Pattern string
Options string
}

// Timestamp represents BSON type Timestamp.
Timestamp uint64
)

Expand Down
2 changes: 2 additions & 0 deletions internal/wire/op_msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ func (msg *OpMsg) MarshalBinary() ([]byte, error) {
}

// String returns a string representation for logging.
//
// Currently, it uses FJSON, but that may change in the future.
func (msg *OpMsg) String() string {
if msg == nil {
return "<nil>"
Expand Down
2 changes: 2 additions & 0 deletions internal/wire/op_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ func (query *OpQuery) MarshalBinary() ([]byte, error) {
}

// String returns a string representation for logging.
//
// Currently, it uses FJSON, but that may change in the future.
func (query *OpQuery) String() string {
if query == nil {
return "<nil>"
Expand Down
2 changes: 2 additions & 0 deletions internal/wire/op_reply.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ func (reply *OpReply) MarshalBinary() ([]byte, error) {
}

// String returns a string representation for logging.
//
// Currently, it uses FJSON, but that may change in the future.
func (reply *OpReply) String() string {
if reply == nil {
return "<nil>"
Expand Down
16 changes: 16 additions & 0 deletions internal/wire/wire.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2021 FerretDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package wire provides wire protocol implementation.
package wire

0 comments on commit 8e6d03b

Please sign in to comment.