2021-04-18 15:41:13 -04:00
// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
2018-10-04 20:44:06 -04:00
package cmd
import (
2020-02-01 21:11:29 -05:00
"bufio"
2021-05-19 17:38:30 -04:00
"context"
2020-10-28 12:18:35 -04:00
"encoding/binary"
2019-03-18 16:07:58 -04:00
"encoding/gob"
"encoding/hex"
2019-02-13 18:29:46 -05:00
"errors"
2018-10-04 20:44:06 -04:00
"fmt"
"io"
2019-03-18 16:07:58 -04:00
"net/http"
2019-12-26 01:05:54 -05:00
"os/user"
2018-10-04 20:44:06 -04:00
"path"
2023-12-01 19:13:19 -05:00
"runtime"
2021-11-08 11:41:27 -05:00
"runtime/debug"
2018-10-04 20:44:06 -04:00
"strconv"
2019-12-23 19:31:03 -05:00
"strings"
2021-05-18 20:25:00 -04:00
"sync"
2018-10-04 20:44:06 -04:00
"time"
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
"github.com/minio/minio/internal/grid"
2020-11-02 20:07:52 -05:00
"github.com/tinylib/msgp/msgp"
2021-11-05 15:20:08 -04:00
jwtreq "github.com/golang-jwt/jwt/v4/request"
2023-06-19 20:53:08 -04:00
"github.com/minio/madmin-go/v3"
2021-06-01 17:59:40 -04:00
"github.com/minio/minio/internal/config"
xhttp "github.com/minio/minio/internal/http"
2021-11-02 11:11:50 -04:00
xioutil "github.com/minio/minio/internal/ioutil"
2021-06-01 17:59:40 -04:00
xjwt "github.com/minio/minio/internal/jwt"
"github.com/minio/minio/internal/logger"
2023-01-23 06:12:47 -05:00
"github.com/minio/mux"
2024-05-24 19:05:23 -04:00
xnet "github.com/minio/pkg/v3/net"
2018-10-04 20:44:06 -04:00
)
2022-08-04 19:10:08 -04:00
var errDiskStale = errors . New ( "drive stale" )
2019-02-13 18:29:46 -05:00
2018-10-04 20:44:06 -04:00
// To abstract a disk over network.
type storageRESTServer struct {
2024-02-28 02:02:14 -05:00
endpoint Endpoint
2023-12-01 15:01:14 -05:00
}
2024-02-19 17:54:46 -05:00
var (
2024-06-10 11:51:27 -04:00
storageCheckPartsRPC = grid . NewSingleHandler [ * CheckPartsHandlerParams , * CheckPartsResp ] ( grid . HandlerCheckParts2 , func ( ) * CheckPartsHandlerParams { return & CheckPartsHandlerParams { } } , func ( ) * CheckPartsResp { return & CheckPartsResp { } } )
2024-04-01 19:42:09 -04:00
storageDeleteFileRPC = grid . NewSingleHandler [ * DeleteFileHandlerParams , grid . NoPayload ] ( grid . HandlerDeleteFile , func ( ) * DeleteFileHandlerParams { return & DeleteFileHandlerParams { } } , grid . NewNoPayload ) . AllowCallRequestPool ( true )
storageDeleteVersionRPC = grid . NewSingleHandler [ * DeleteVersionHandlerParams , grid . NoPayload ] ( grid . HandlerDeleteVersion , func ( ) * DeleteVersionHandlerParams { return & DeleteVersionHandlerParams { } } , grid . NewNoPayload )
storageDiskInfoRPC = grid . NewSingleHandler [ * DiskInfoOptions , * DiskInfo ] ( grid . HandlerDiskInfo , func ( ) * DiskInfoOptions { return & DiskInfoOptions { } } , func ( ) * DiskInfo { return & DiskInfo { } } ) . WithSharedResponse ( ) . AllowCallRequestPool ( true )
storageNSScannerRPC = grid . NewStream [ * nsScannerOptions , grid . NoPayload , * nsScannerResp ] ( grid . HandlerNSScanner , func ( ) * nsScannerOptions { return & nsScannerOptions { } } , nil , func ( ) * nsScannerResp { return & nsScannerResp { } } )
storageReadAllRPC = grid . NewSingleHandler [ * ReadAllHandlerParams , * grid . Bytes ] ( grid . HandlerReadAll , func ( ) * ReadAllHandlerParams { return & ReadAllHandlerParams { } } , grid . NewBytes ) . AllowCallRequestPool ( true )
storageWriteAllRPC = grid . NewSingleHandler [ * WriteAllHandlerParams , grid . NoPayload ] ( grid . HandlerWriteAll , func ( ) * WriteAllHandlerParams { return & WriteAllHandlerParams { } } , grid . NewNoPayload )
storageReadVersionRPC = grid . NewSingleHandler [ * grid . MSS , * FileInfo ] ( grid . HandlerReadVersion , grid . NewMSS , func ( ) * FileInfo { return & FileInfo { } } )
storageReadXLRPC = grid . NewSingleHandler [ * grid . MSS , * RawFileInfo ] ( grid . HandlerReadXL , grid . NewMSS , func ( ) * RawFileInfo { return & RawFileInfo { } } )
2024-04-23 13:15:52 -04:00
storageRenameDataRPC = grid . NewSingleHandler [ * RenameDataHandlerParams , * RenameDataResp ] ( grid . HandlerRenameData2 , func ( ) * RenameDataHandlerParams { return & RenameDataHandlerParams { } } , func ( ) * RenameDataResp { return & RenameDataResp { } } )
2024-04-01 19:42:09 -04:00
storageRenameDataInlineRPC = grid . NewSingleHandler [ * RenameDataInlineHandlerParams , * RenameDataResp ] ( grid . HandlerRenameDataInline , newRenameDataInlineHandlerParams , func ( ) * RenameDataResp { return & RenameDataResp { } } ) . AllowCallRequestPool ( false )
storageRenameFileRPC = grid . NewSingleHandler [ * RenameFileHandlerParams , grid . NoPayload ] ( grid . HandlerRenameFile , func ( ) * RenameFileHandlerParams { return & RenameFileHandlerParams { } } , grid . NewNoPayload ) . AllowCallRequestPool ( true )
storageStatVolRPC = grid . NewSingleHandler [ * grid . MSS , * VolInfo ] ( grid . HandlerStatVol , grid . NewMSS , func ( ) * VolInfo { return & VolInfo { } } )
storageUpdateMetadataRPC = grid . NewSingleHandler [ * MetadataHandlerParams , grid . NoPayload ] ( grid . HandlerUpdateMetadata , func ( ) * MetadataHandlerParams { return & MetadataHandlerParams { } } , grid . NewNoPayload )
storageWriteMetadataRPC = grid . NewSingleHandler [ * MetadataHandlerParams , grid . NoPayload ] ( grid . HandlerWriteMetadata , func ( ) * MetadataHandlerParams { return & MetadataHandlerParams { } } , grid . NewNoPayload )
storageListDirRPC = grid . NewStream [ * grid . MSS , grid . NoPayload , * ListDirResult ] ( grid . HandlerListDir , grid . NewMSS , nil , func ( ) * ListDirResult { return & ListDirResult { } } ) . WithOutCapacity ( 1 )
2024-02-19 17:54:46 -05:00
)
2024-02-28 02:02:14 -05:00
func getStorageViaEndpoint ( endpoint Endpoint ) StorageAPI {
2023-12-13 22:27:55 -05:00
globalLocalDrivesMu . RLock ( )
defer globalLocalDrivesMu . RUnlock ( )
2024-02-28 16:23:50 -05:00
if len ( globalLocalSetDrives ) == 0 {
2024-05-22 19:07:14 -04:00
return globalLocalDrivesMap [ endpoint . String ( ) ]
2024-02-28 16:23:50 -05:00
}
2024-02-28 02:02:14 -05:00
return globalLocalSetDrives [ endpoint . PoolIdx ] [ endpoint . SetIdx ] [ endpoint . DiskIdx ]
}
func ( s * storageRESTServer ) getStorage ( ) StorageAPI {
return getStorageViaEndpoint ( s . endpoint )
2018-10-04 20:44:06 -04:00
}
func ( s * storageRESTServer ) writeErrorResponse ( w http . ResponseWriter , err error ) {
2023-07-14 21:34:55 -04:00
err = unwrapAll ( err )
switch err {
case errDiskStale :
2020-06-17 17:49:26 -04:00
w . WriteHeader ( http . StatusPreconditionFailed )
2023-07-14 21:34:55 -04:00
case errFileNotFound , errFileVersionNotFound :
w . WriteHeader ( http . StatusNotFound )
case errInvalidAccessKeyID , errAccessKeyDisabled , errNoAuthToken , errMalformedAuth , errAuthentication , errSkewedAuthTime :
w . WriteHeader ( http . StatusUnauthorized )
2023-08-22 14:10:41 -04:00
case context . Canceled , context . DeadlineExceeded :
w . WriteHeader ( 499 )
2023-07-14 21:34:55 -04:00
default :
2020-06-17 17:49:26 -04:00
w . WriteHeader ( http . StatusForbidden )
}
2018-10-04 20:44:06 -04:00
w . Write ( [ ] byte ( err . Error ( ) ) )
}
2019-04-18 02:16:27 -04:00
// DefaultSkewTime - skew time is 15 minutes between minio peers.
const DefaultSkewTime = 15 * time . Minute
2019-02-13 18:29:46 -05:00
// Authenticates storage client's requests and validates for skewed time.
func storageServerRequestValidate ( r * http . Request ) error {
2020-01-30 21:59:22 -05:00
token , err := jwtreq . AuthorizationHeaderExtractor . ExtractToken ( r )
2019-04-03 15:16:19 -04:00
if err != nil {
2020-01-30 21:59:22 -05:00
if err == jwtreq . ErrNoTokenInRequest {
return errNoAuthToken
}
2023-07-14 21:34:55 -04:00
return errMalformedAuth
2019-02-12 16:24:14 -05:00
}
2020-01-30 21:59:22 -05:00
claims := xjwt . NewStandardClaims ( )
if err = xjwt . ParseWithStandardClaims ( token , claims , [ ] byte ( globalActiveCred . SecretKey ) ) ; err != nil {
return errAuthentication
}
owner := claims . AccessKey == globalActiveCred . AccessKey || claims . Subject == globalActiveCred . AccessKey
if ! owner {
return errAuthentication
}
2020-11-02 18:15:12 -05:00
if claims . Audience != r . URL . RawQuery {
2020-01-30 21:59:22 -05:00
return errAuthentication
}
2018-10-04 20:44:06 -04:00
requestTimeStr := r . Header . Get ( "X-Minio-Time" )
requestTime , err := time . Parse ( time . RFC3339 , requestTimeStr )
if err != nil {
2023-07-14 21:34:55 -04:00
return errMalformedAuth
2018-10-04 20:44:06 -04:00
}
utcNow := UTCNow ( )
delta := requestTime . Sub ( utcNow )
if delta < 0 {
2020-01-30 21:59:22 -05:00
delta *= - 1
2018-10-04 20:44:06 -04:00
}
if delta > DefaultSkewTime {
2023-07-14 21:34:55 -04:00
return errSkewedAuthTime
2019-02-13 18:29:46 -05:00
}
2020-01-30 21:59:22 -05:00
2019-02-13 18:29:46 -05:00
return nil
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// IsAuthValid - To authenticate and verify the time difference.
2022-03-01 18:06:47 -05:00
func ( s * storageRESTServer ) IsAuthValid ( w http . ResponseWriter , r * http . Request ) bool {
2023-12-01 15:01:14 -05:00
if s . getStorage ( ) == nil {
2020-09-23 15:00:29 -04:00
s . writeErrorResponse ( w , errDiskNotFound )
return false
}
2019-02-13 18:29:46 -05:00
if err := storageServerRequestValidate ( r ) ; err != nil {
s . writeErrorResponse ( w , err )
return false
}
2020-09-17 00:14:35 -04:00
2022-03-01 18:06:47 -05:00
return true
}
// IsValid - To authenticate and check if the disk-id in the request corresponds to the underlying disk.
func ( s * storageRESTServer ) IsValid ( w http . ResponseWriter , r * http . Request ) bool {
if ! s . IsAuthValid ( w , r ) {
return false
}
2021-12-09 11:38:46 -05:00
if err := r . ParseForm ( ) ; err != nil {
s . writeErrorResponse ( w , err )
return false
}
2021-08-08 01:43:01 -04:00
diskID := r . Form . Get ( storageRESTDiskID )
2019-10-25 13:37:53 -04:00
if diskID == "" {
// Request sent empty disk-id, we allow the request
// as the peer might be coming up and trying to read format.json
// or create format.json
return true
2018-10-04 20:44:06 -04:00
}
2020-09-17 00:14:35 -04:00
2023-12-01 15:01:14 -05:00
storedDiskID , err := s . getStorage ( ) . GetDiskID ( )
2020-07-21 16:54:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return false
2019-02-13 18:29:46 -05:00
}
2020-07-21 16:54:06 -04:00
if diskID != storedDiskID {
s . writeErrorResponse ( w , errDiskStale )
return false
}
// If format.json is available and request sent the right disk-id, we allow the request
return true
2019-02-13 18:29:46 -05:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// checkID - check if the disk-id in the request corresponds to the underlying disk.
func ( s * storageRESTServer ) checkID ( wantID string ) bool {
2023-12-01 15:01:14 -05:00
if s . getStorage ( ) == nil {
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return false
}
if wantID == "" {
// Request sent empty disk-id, we allow the request
// as the peer might be coming up and trying to read format.json
// or create format.json
return true
}
2023-12-01 15:01:14 -05:00
storedDiskID , err := s . getStorage ( ) . GetDiskID ( )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
if err != nil {
return false
}
return wantID == storedDiskID
}
2020-06-17 17:49:26 -04:00
// HealthHandler handler checks if disk is stale
func ( s * storageRESTServer ) HealthHandler ( w http . ResponseWriter , r * http . Request ) {
s . IsValid ( w , r )
}
2018-10-04 20:44:06 -04:00
// DiskInfoHandler - returns disk info.
2024-01-25 15:45:46 -05:00
func ( s * storageRESTServer ) DiskInfoHandler ( opts * DiskInfoOptions ) ( * DiskInfo , * grid . RemoteErr ) {
if ! s . checkID ( opts . DiskID ) {
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return nil , grid . NewRemoteErr ( errDiskNotFound )
2018-10-04 20:44:06 -04:00
}
2024-01-25 15:45:46 -05:00
info , err := s . getStorage ( ) . DiskInfo ( context . Background ( ) , * opts )
2018-10-04 20:44:06 -04:00
if err != nil {
2020-07-13 12:51:07 -04:00
info . Error = err . Error ( )
2018-10-04 20:44:06 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return & info , nil
2018-10-04 20:44:06 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
func ( s * storageRESTServer ) NSScannerHandler ( ctx context . Context , params * nsScannerOptions , out chan <- * nsScannerResp ) * grid . RemoteErr {
if ! s . checkID ( params . DiskID ) {
return grid . NewRemoteErr ( errDiskNotFound )
2022-04-07 11:10:40 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
if params . Cache == nil {
return grid . NewRemoteErrString ( "NSScannerHandler: provided cache is nil" )
2019-12-12 09:02:37 -05:00
}
2021-05-19 17:38:30 -04:00
// Collect updates, stream them before the full cache is sent.
updates := make ( chan dataUsageEntry , 1 )
var wg sync . WaitGroup
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
for update := range updates {
2024-02-19 17:54:46 -05:00
resp := storageNSScannerRPC . NewResponse ( )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
resp . Update = & update
out <- resp
2021-05-19 17:38:30 -04:00
}
} ( )
2024-01-02 16:51:24 -05:00
ui , err := s . getStorage ( ) . NSScanner ( ctx , * params . Cache , updates , madmin . HealScanMode ( params . ScanMode ) , nil )
2021-05-19 17:38:30 -04:00
wg . Wait ( )
2021-07-02 14:19:56 -04:00
if err != nil {
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return grid . NewRemoteErr ( err )
2021-07-02 14:19:56 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// Send final response.
2024-02-19 17:54:46 -05:00
resp := storageNSScannerRPC . NewResponse ( )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
resp . Final = & ui
out <- resp
return nil
2019-12-12 09:02:37 -05:00
}
2018-10-04 20:44:06 -04:00
// MakeVolHandler - make a volume.
func ( s * storageRESTServer ) MakeVolHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
2023-12-01 15:01:14 -05:00
err := s . getStorage ( ) . MakeVol ( r . Context ( ) , volume )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
}
}
2019-12-23 19:31:03 -05:00
// MakeVolBulkHandler - create multiple volumes as a bulk operation.
func ( s * storageRESTServer ) MakeVolBulkHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volumes := strings . Split ( r . Form . Get ( storageRESTVolumes ) , "," )
2023-12-01 15:01:14 -05:00
err := s . getStorage ( ) . MakeVolBulk ( r . Context ( ) , volumes ... )
2019-12-23 19:31:03 -05:00
if err != nil {
s . writeErrorResponse ( w , err )
}
}
2018-10-04 20:44:06 -04:00
// StatVolHandler - stat a volume.
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
func ( s * storageRESTServer ) StatVolHandler ( params * grid . MSS ) ( * VolInfo , * grid . RemoteErr ) {
if ! s . checkID ( params . Get ( storageRESTDiskID ) ) {
return nil , grid . NewRemoteErr ( errDiskNotFound )
2018-10-04 20:44:06 -04:00
}
2023-12-01 15:01:14 -05:00
info , err := s . getStorage ( ) . StatVol ( context . Background ( ) , params . Get ( storageRESTVolume ) )
2018-10-04 20:44:06 -04:00
if err != nil {
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return nil , grid . NewRemoteErr ( err )
2018-10-04 20:44:06 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return & info , nil
2018-10-04 20:44:06 -04:00
}
2019-01-17 07:58:18 -05:00
// AppendFileHandler - append data from the request to the file specified.
func ( s * storageRESTServer ) AppendFileHandler ( w http . ResponseWriter , r * http . Request ) {
2018-10-04 20:44:06 -04:00
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
2019-01-17 07:58:18 -05:00
buf := make ( [ ] byte , r . ContentLength )
_ , err := io . ReadFull ( r . Body , buf )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2023-12-01 15:01:14 -05:00
err = s . getStorage ( ) . AppendFile ( r . Context ( ) , volume , filePath , buf )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
}
}
2021-04-18 15:41:13 -04:00
// CreateFileHandler - copy the contents from the request.
2019-01-17 07:58:18 -05:00
func ( s * storageRESTServer ) CreateFileHandler ( w http . ResponseWriter , r * http . Request ) {
2018-10-04 20:44:06 -04:00
if ! s . IsValid ( w , r ) {
return
}
2024-01-30 15:43:25 -05:00
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
2024-01-30 15:43:25 -05:00
origvolume := r . Form . Get ( storageRESTOrigVolume )
2018-10-04 20:44:06 -04:00
2021-12-09 11:38:46 -05:00
fileSizeStr := r . Form . Get ( storageRESTLength )
2019-01-17 07:58:18 -05:00
fileSize , err := strconv . Atoi ( fileSizeStr )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2021-03-24 12:05:03 -04:00
2021-08-27 12:16:36 -04:00
done , body := keepHTTPReqResponseAlive ( w , r )
2024-01-30 15:43:25 -05:00
done ( s . getStorage ( ) . CreateFile ( r . Context ( ) , origvolume , volume , filePath , int64 ( fileSize ) , body ) )
2018-10-04 20:44:06 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// DeleteVersionHandler delete updated metadata.
func ( s * storageRESTServer ) DeleteVersionHandler ( p * DeleteVersionHandlerParams ) ( np grid . NoPayload , gerr * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return np , grid . NewRemoteErr ( errDiskNotFound )
2020-06-26 19:49:49 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
volume := p . Volume
filePath := p . FilePath
forceDelMarker := p . ForceDelMarker
2020-06-26 19:49:49 -04:00
2023-12-29 18:52:41 -05:00
opts := DeleteOptions { }
err := s . getStorage ( ) . DeleteVersion ( context . Background ( ) , volume , filePath , p . FI , forceDelMarker , opts )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return np , grid . NewRemoteErr ( err )
}
// ReadVersionHandlerWS read metadata of versionID
func ( s * storageRESTServer ) ReadVersionHandlerWS ( params * grid . MSS ) ( * FileInfo , * grid . RemoteErr ) {
if ! s . checkID ( params . Get ( storageRESTDiskID ) ) {
return nil , grid . NewRemoteErr ( errDiskNotFound )
}
2024-01-30 15:43:25 -05:00
origvolume := params . Get ( storageRESTOrigVolume )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
volume := params . Get ( storageRESTVolume )
filePath := params . Get ( storageRESTFilePath )
versionID := params . Get ( storageRESTVersionID )
readData , err := strconv . ParseBool ( params . Get ( storageRESTReadData ) )
if err != nil {
return nil , grid . NewRemoteErr ( err )
2020-06-23 13:20:31 -04:00
}
2023-11-21 00:33:47 -05:00
healing , err := strconv . ParseBool ( params . Get ( storageRESTHealing ) )
if err != nil {
return nil , grid . NewRemoteErr ( err )
}
2024-06-17 10:29:18 -04:00
inclFreeVersions , err := strconv . ParseBool ( params . Get ( storageRESTInclFreeVersions ) )
if err != nil {
return nil , grid . NewRemoteErr ( err )
}
fi , err := s . getStorage ( ) . ReadVersion ( context . Background ( ) , origvolume , volume , filePath , versionID , ReadOptions {
InclFreeVersions : inclFreeVersions ,
ReadData : readData ,
Healing : healing ,
} )
2020-06-12 23:04:01 -04:00
if err != nil {
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return nil , grid . NewRemoteErr ( err )
2020-06-12 23:04:01 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return & fi , nil
2020-06-12 23:04:01 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// ReadVersionHandler read metadata of versionID
2020-06-12 23:04:01 -04:00
func ( s * storageRESTServer ) ReadVersionHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
2024-01-30 15:43:25 -05:00
origvolume := r . Form . Get ( storageRESTOrigVolume )
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
versionID := r . Form . Get ( storageRESTVersionID )
readData , err := strconv . ParseBool ( r . Form . Get ( storageRESTReadData ) )
2020-06-12 23:04:01 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2023-11-21 00:33:47 -05:00
healing , err := strconv . ParseBool ( r . Form . Get ( storageRESTHealing ) )
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2024-06-17 10:29:18 -04:00
inclFreeVersions , err := strconv . ParseBool ( r . Form . Get ( storageRESTInclFreeVersions ) )
if err != nil {
s . writeErrorResponse ( w , err )
return
}
fi , err := s . getStorage ( ) . ReadVersion ( r . Context ( ) , origvolume , volume , filePath , versionID , ReadOptions {
InclFreeVersions : inclFreeVersions ,
ReadData : readData ,
Healing : healing ,
} )
2021-01-07 22:27:31 -05:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2024-04-04 08:04:40 -04:00
storageLogIf ( r . Context ( ) , msgp . Encode ( w , & fi ) )
2020-06-12 23:04:01 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// WriteMetadataHandler rpc handler to write new updated metadata.
func ( s * storageRESTServer ) WriteMetadataHandler ( p * MetadataHandlerParams ) ( np grid . NoPayload , gerr * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return grid . NewNPErr ( errDiskNotFound )
2020-06-12 23:04:01 -04:00
}
2024-01-30 15:43:25 -05:00
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
volume := p . Volume
filePath := p . FilePath
2024-01-30 15:43:25 -05:00
origvolume := p . OrigVolume
2020-06-12 23:04:01 -04:00
2024-01-30 15:43:25 -05:00
err := s . getStorage ( ) . WriteMetadata ( context . Background ( ) , origvolume , volume , filePath , p . FI )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
return np , grid . NewRemoteErr ( err )
2020-06-12 23:04:01 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// UpdateMetadataHandler update new updated metadata.
func ( s * storageRESTServer ) UpdateMetadataHandler ( p * MetadataHandlerParams ) ( grid . NoPayload , * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return grid . NewNPErr ( errDiskNotFound )
2021-04-04 16:32:31 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
volume := p . Volume
filePath := p . FilePath
2021-04-04 16:32:31 -04:00
2023-12-01 15:01:14 -05:00
return grid . NewNPErr ( s . getStorage ( ) . UpdateMetadata ( context . Background ( ) , volume , filePath , p . FI , p . UpdateOpts ) )
2021-04-04 16:32:31 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// CheckPartsHandler - check if a file metadata exists.
2024-06-10 11:51:27 -04:00
func ( s * storageRESTServer ) CheckPartsHandler ( p * CheckPartsHandlerParams ) ( * CheckPartsResp , * grid . RemoteErr ) {
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
if ! s . checkID ( p . DiskID ) {
2024-06-10 11:51:27 -04:00
return nil , grid . NewRemoteErr ( errDiskNotFound )
2020-06-12 23:04:01 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
volume := p . Volume
filePath := p . FilePath
2024-06-10 11:51:27 -04:00
resp , err := s . getStorage ( ) . CheckParts ( context . Background ( ) , volume , filePath , p . FI )
return resp , grid . NewRemoteErr ( err )
2020-06-12 23:04:01 -04:00
}
2024-02-28 12:54:52 -05:00
func ( s * storageRESTServer ) WriteAllHandler ( p * WriteAllHandlerParams ) ( grid . NoPayload , * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return grid . NewNPErr ( errDiskNotFound )
}
volume := p . Volume
filePath := p . FilePath
return grid . NewNPErr ( s . getStorage ( ) . WriteAll ( context . Background ( ) , volume , filePath , p . Buf ) )
}
2018-10-04 20:44:06 -04:00
// ReadAllHandler - read all the contents of a file.
2024-01-25 15:45:46 -05:00
func ( s * storageRESTServer ) ReadAllHandler ( p * ReadAllHandlerParams ) ( * grid . Bytes , * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return nil , grid . NewRemoteErr ( errDiskNotFound )
2018-10-04 20:44:06 -04:00
}
2024-01-25 15:45:46 -05:00
volume := p . Volume
filePath := p . FilePath
buf , err := s . getStorage ( ) . ReadAll ( context . Background ( ) , volume , filePath )
return grid . NewBytesWith ( buf ) , grid . NewRemoteErr ( err )
2018-10-04 20:44:06 -04:00
}
2022-04-20 15:49:05 -04:00
// ReadXLHandler - read xl.meta for an object at path.
func ( s * storageRESTServer ) ReadXLHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
readData , err := strconv . ParseBool ( r . Form . Get ( storageRESTReadData ) )
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2023-12-01 15:01:14 -05:00
rf , err := s . getStorage ( ) . ReadXL ( r . Context ( ) , volume , filePath , readData )
2022-04-20 15:49:05 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2024-04-04 08:04:40 -04:00
storageLogIf ( r . Context ( ) , msgp . Encode ( w , & rf ) )
2022-04-20 15:49:05 -04:00
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// ReadXLHandlerWS - read xl.meta for an object at path.
func ( s * storageRESTServer ) ReadXLHandlerWS ( params * grid . MSS ) ( * RawFileInfo , * grid . RemoteErr ) {
if ! s . checkID ( params . Get ( storageRESTDiskID ) ) {
return nil , grid . NewRemoteErr ( errDiskNotFound )
}
volume := params . Get ( storageRESTVolume )
filePath := params . Get ( storageRESTFilePath )
readData , err := strconv . ParseBool ( params . Get ( storageRESTReadData ) )
if err != nil {
return nil , grid . NewRemoteErr ( err )
}
2023-12-01 15:01:14 -05:00
rf , err := s . getStorage ( ) . ReadXL ( context . Background ( ) , volume , filePath , readData )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
if err != nil {
return nil , grid . NewRemoteErr ( err )
}
return & rf , nil
}
2018-10-04 20:44:06 -04:00
// ReadFileHandler - read section of a file.
func ( s * storageRESTServer ) ReadFileHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
offset , err := strconv . Atoi ( r . Form . Get ( storageRESTOffset ) )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2021-12-09 11:38:46 -05:00
length , err := strconv . Atoi ( r . Form . Get ( storageRESTLength ) )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
if offset < 0 || length < 0 {
s . writeErrorResponse ( w , errInvalidArgument )
return
}
var verifier * BitrotVerifier
2021-12-09 11:38:46 -05:00
if r . Form . Get ( storageRESTBitrotAlgo ) != "" {
hashStr := r . Form . Get ( storageRESTBitrotHash )
2018-10-04 20:44:06 -04:00
var hash [ ] byte
hash , err = hex . DecodeString ( hashStr )
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2021-12-09 11:38:46 -05:00
verifier = NewBitrotVerifier ( BitrotAlgorithmFromString ( r . Form . Get ( storageRESTBitrotAlgo ) ) , hash )
2018-10-04 20:44:06 -04:00
}
buf := make ( [ ] byte , length )
2021-10-28 20:02:22 -04:00
defer metaDataPoolPut ( buf ) // Reuse if we can.
2023-12-01 15:01:14 -05:00
_ , err = s . getStorage ( ) . ReadFile ( r . Context ( ) , volume , filePath , int64 ( offset ) , buf , verifier )
2018-10-04 20:44:06 -04:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2019-07-03 01:34:32 -04:00
w . Header ( ) . Set ( xhttp . ContentLength , strconv . Itoa ( len ( buf ) ) )
2018-10-04 20:44:06 -04:00
w . Write ( buf )
}
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// ReadFileStreamHandler - read section of a file.
2019-01-17 07:58:18 -05:00
func ( s * storageRESTServer ) ReadFileStreamHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
offset , err := strconv . Atoi ( r . Form . Get ( storageRESTOffset ) )
2019-01-17 07:58:18 -05:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2021-12-09 11:38:46 -05:00
length , err := strconv . Atoi ( r . Form . Get ( storageRESTLength ) )
2019-01-17 07:58:18 -05:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
2019-03-18 01:20:26 -04:00
2023-11-10 13:10:14 -05:00
w . Header ( ) . Set ( xhttp . ContentLength , strconv . Itoa ( length ) )
2023-12-01 15:01:14 -05:00
rc , err := s . getStorage ( ) . ReadFileStream ( r . Context ( ) , volume , filePath , int64 ( offset ) , int64 ( length ) )
2019-01-17 07:58:18 -05:00
if err != nil {
s . writeErrorResponse ( w , err )
return
}
defer rc . Close ( )
2019-09-26 02:08:24 -04:00
2023-11-10 13:10:14 -05:00
rf , ok := w . ( io . ReaderFrom )
2023-12-01 19:13:19 -05:00
if ok && runtime . GOOS != "windows" {
2023-11-10 13:10:14 -05:00
// Attempt to use splice/sendfile() optimization, A very specific behavior mentioned below is necessary.
// See https://github.com/golang/go/blob/f7c5cbb82087c55aa82081e931e0142783700ce8/src/net/sendfile_linux.go#L20
2023-12-01 19:13:19 -05:00
// Windows can lock up with this optimization, so we fall back to regular copy.
2023-12-14 13:51:57 -05:00
sr , ok := rc . ( * sendFileReader )
2023-11-10 13:10:14 -05:00
if ok {
2024-04-08 05:22:27 -04:00
// Sendfile sends in 4MiB chunks per sendfile syscall which is more than enough
// for most setups.
2023-12-14 13:51:57 -05:00
_ , err = rf . ReadFrom ( sr . Reader )
if ! xnet . IsNetworkOrHostDown ( err , true ) { // do not need to log disconnected clients
2024-04-04 08:04:40 -04:00
storageLogIf ( r . Context ( ) , err )
2023-12-14 13:51:57 -05:00
}
if err == nil || ! errors . Is ( err , xhttp . ErrNotImplemented ) {
return
2023-11-10 13:10:14 -05:00
}
2021-02-28 18:33:03 -05:00
}
2023-11-10 13:10:14 -05:00
} // Fallback to regular copy
_ , err = xioutil . Copy ( w , rc )
if ! xnet . IsNetworkOrHostDown ( err , true ) { // do not need to log disconnected clients
2024-04-04 08:04:40 -04:00
storageLogIf ( r . Context ( ) , err )
2021-02-28 18:33:03 -05:00
}
2019-01-17 07:58:18 -05:00
}
2018-10-04 20:44:06 -04:00
// ListDirHandler - list a directory.
2024-02-19 17:54:46 -05:00
func ( s * storageRESTServer ) ListDirHandler ( ctx context . Context , params * grid . MSS , out chan <- * ListDirResult ) * grid . RemoteErr {
if ! s . checkID ( params . Get ( storageRESTDiskID ) ) {
return grid . NewRemoteErr ( errDiskNotFound )
2018-10-04 20:44:06 -04:00
}
2024-02-19 17:54:46 -05:00
volume := params . Get ( storageRESTVolume )
dirPath := params . Get ( storageRESTDirPath )
origvolume := params . Get ( storageRESTOrigVolume )
count , err := strconv . Atoi ( params . Get ( storageRESTCount ) )
2018-10-04 20:44:06 -04:00
if err != nil {
2024-02-19 17:54:46 -05:00
return grid . NewRemoteErr ( err )
2018-10-04 20:44:06 -04:00
}
2020-03-22 01:10:13 -04:00
2024-02-19 17:54:46 -05:00
entries , err := s . getStorage ( ) . ListDir ( ctx , origvolume , volume , dirPath , count )
2018-10-04 20:44:06 -04:00
if err != nil {
2024-02-19 17:54:46 -05:00
return grid . NewRemoteErr ( err )
2018-10-04 20:44:06 -04:00
}
2024-02-19 17:54:46 -05:00
out <- & ListDirResult { Entries : entries }
return nil
2018-10-04 20:44:06 -04:00
}
// DeleteFileHandler - delete a file.
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
func ( s * storageRESTServer ) DeleteFileHandler ( p * DeleteFileHandlerParams ) ( grid . NoPayload , * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return grid . NewNPErr ( errDiskNotFound )
2018-10-04 20:44:06 -04:00
}
2023-12-01 15:01:14 -05:00
return grid . NewNPErr ( s . getStorage ( ) . Delete ( context . Background ( ) , p . Volume , p . FilePath , p . Opts ) )
2018-10-04 20:44:06 -04:00
}
2020-06-12 23:04:01 -04:00
// DeleteVersionsErrsResp - collection of delete errors
// for bulk version deletes
type DeleteVersionsErrsResp struct {
2019-09-30 22:01:28 -04:00
Errs [ ] error
}
2020-06-12 23:04:01 -04:00
// DeleteVersionsHandler - delete a set of a versions.
func ( s * storageRESTServer ) DeleteVersionsHandler ( w http . ResponseWriter , r * http . Request ) {
2019-05-13 15:25:49 -04:00
if ! s . IsValid ( w , r ) {
return
}
2020-06-12 23:04:01 -04:00
2021-08-08 01:43:01 -04:00
volume := r . Form . Get ( storageRESTVolume )
totalVersions , err := strconv . Atoi ( r . Form . Get ( storageRESTTotalVersions ) )
2020-06-12 23:04:01 -04:00
if err != nil {
2020-02-01 21:11:29 -05:00
s . writeErrorResponse ( w , err )
return
}
2019-05-13 15:25:49 -04:00
2021-11-01 13:50:07 -04:00
versions := make ( [ ] FileInfoVersions , totalVersions )
2023-07-06 19:02:08 -04:00
decoder := msgpNewReader ( r . Body )
defer readMsgpReaderPoolPut ( decoder )
2020-06-12 23:04:01 -04:00
for i := 0 ; i < totalVersions ; i ++ {
2020-11-02 20:07:52 -05:00
dst := & versions [ i ]
if err := dst . DecodeMsg ( decoder ) ; err != nil {
2020-06-12 23:04:01 -04:00
s . writeErrorResponse ( w , err )
return
}
}
dErrsResp := & DeleteVersionsErrsResp { Errs : make ( [ ] error , totalVersions ) }
2020-03-22 01:10:13 -04:00
2020-07-30 22:45:12 -04:00
setEventStreamHeaders ( w )
2020-03-11 11:56:36 -04:00
encoder := gob . NewEncoder ( w )
2020-03-18 19:19:29 -04:00
done := keepHTTPResponseAlive ( w )
2023-12-13 22:27:55 -05:00
2023-12-29 18:52:41 -05:00
opts := DeleteOptions { }
errs := s . getStorage ( ) . DeleteVersions ( r . Context ( ) , volume , versions , opts )
2020-05-11 23:41:38 -04:00
done ( nil )
2020-06-12 23:04:01 -04:00
for idx := range versions {
if errs [ idx ] != nil {
dErrsResp . Errs [ idx ] = StorageErr ( errs [ idx ] . Error ( ) )
2019-09-30 22:01:28 -04:00
}
}
2020-03-11 11:56:36 -04:00
encoder . Encode ( dErrsResp )
}
2020-06-12 23:04:01 -04:00
// RenameDataHandler - renames a meta object and data dir to destination.
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
func ( s * storageRESTServer ) RenameDataHandler ( p * RenameDataHandlerParams ) ( * RenameDataResp , * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return nil , grid . NewRemoteErr ( errDiskNotFound )
2021-04-20 13:44:39 -04:00
}
2024-04-23 13:15:52 -04:00
resp , err := s . getStorage ( ) . RenameData ( context . Background ( ) , p . SrcVolume , p . SrcPath , p . FI , p . DstVolume , p . DstPath , p . Opts )
return & resp , grid . NewRemoteErr ( err )
2024-04-01 19:42:09 -04:00
}
// RenameDataInlineHandler - renames a meta object and data dir to destination.
func ( s * storageRESTServer ) RenameDataInlineHandler ( p * RenameDataInlineHandlerParams ) ( * RenameDataResp , * grid . RemoteErr ) {
defer p . Recycle ( )
return s . RenameDataHandler ( & p . RenameDataHandlerParams )
2019-05-13 15:25:49 -04:00
}
2024-01-25 15:45:46 -05:00
// RenameFileHandler - rename a file from source to destination
func ( s * storageRESTServer ) RenameFileHandler ( p * RenameFileHandlerParams ) ( grid . NoPayload , * grid . RemoteErr ) {
if ! s . checkID ( p . DiskID ) {
return grid . NewNPErr ( errDiskNotFound )
2018-10-04 20:44:06 -04:00
}
2024-01-25 15:45:46 -05:00
return grid . NewNPErr ( s . getStorage ( ) . RenameFile ( context . Background ( ) , p . SrcVolume , p . SrcFilePath , p . DstVolume , p . DstFilePath ) )
2018-10-04 20:44:06 -04:00
}
2022-11-28 13:20:55 -05:00
// CleanAbandonedDataHandler - Clean unused data directories.
func ( s * storageRESTServer ) CleanAbandonedDataHandler ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
if volume == "" || filePath == "" {
return // Ignore
}
2023-12-01 15:01:14 -05:00
keepHTTPResponseAlive ( w ) ( s . getStorage ( ) . CleanAbandonedData ( r . Context ( ) , volume , filePath ) )
2022-11-28 13:20:55 -05:00
}
2021-08-27 12:16:36 -04:00
// closeNotifier is itself a ReadCloser that will notify when either an error occurs or
// the Close() function is called.
type closeNotifier struct {
rc io . ReadCloser
done chan struct { }
}
func ( c * closeNotifier ) Read ( p [ ] byte ) ( n int , err error ) {
n , err = c . rc . Read ( p )
if err != nil {
if c . done != nil {
2024-01-28 13:04:17 -05:00
xioutil . SafeClose ( c . done )
2021-08-27 12:16:36 -04:00
c . done = nil
}
}
return n , err
}
func ( c * closeNotifier ) Close ( ) error {
if c . done != nil {
2024-01-28 13:04:17 -05:00
xioutil . SafeClose ( c . done )
2021-08-27 12:16:36 -04:00
c . done = nil
}
return c . rc . Close ( )
}
// keepHTTPReqResponseAlive can be used to avoid timeouts with long storage
// operations, such as bitrot verification or data usage scanning.
// Every 10 seconds a space character is sent.
// keepHTTPReqResponseAlive will wait for the returned body to be read before starting the ticker.
// The returned function should always be called to release resources.
// An optional error can be sent which will be picked as text only error,
// without its original type by the receiver.
// waitForHTTPResponse should be used to the receiving side.
func keepHTTPReqResponseAlive ( w http . ResponseWriter , r * http . Request ) ( resp func ( error ) , body io . ReadCloser ) {
bodyDoneCh := make ( chan struct { } )
doneCh := make ( chan error )
ctx := r . Context ( )
go func ( ) {
2022-01-02 12:15:06 -05:00
canWrite := true
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write := func ( b [ ] byte ) {
if canWrite {
n , err := w . Write ( b )
if err != nil || n != len ( b ) {
canWrite = false
}
}
}
2021-08-27 12:16:36 -04:00
// Wait for body to be read.
select {
case <- ctx . Done ( ) :
case <- bodyDoneCh :
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
case err := <- doneCh :
2021-08-27 12:16:36 -04:00
if err != nil {
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 1 } )
write ( [ ] byte ( err . Error ( ) ) )
2021-08-27 12:16:36 -04:00
} else {
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 0 } )
2021-08-27 12:16:36 -04:00
}
2024-01-28 13:04:17 -05:00
xioutil . SafeClose ( doneCh )
2021-08-27 12:16:36 -04:00
return
}
2024-01-28 13:04:17 -05:00
defer xioutil . SafeClose ( doneCh )
2021-08-27 12:16:36 -04:00
// Initiate ticker after body has been read.
ticker := time . NewTicker ( time . Second * 10 )
2024-04-28 13:53:50 -04:00
defer ticker . Stop ( )
2021-08-27 12:16:36 -04:00
for {
select {
case <- ticker . C :
2024-04-28 13:53:50 -04:00
// The done() might have been called
// concurrently, check for it before we
// write the filler byte.
select {
case err := <- doneCh :
if err != nil {
write ( [ ] byte { 1 } )
write ( [ ] byte ( err . Error ( ) ) )
} else {
write ( [ ] byte { 0 } )
}
return
default :
}
2021-08-27 12:16:36 -04:00
// Response not ready, write a filler byte.
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 32 } )
if canWrite {
w . ( http . Flusher ) . Flush ( )
2021-11-18 20:19:58 -05:00
}
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
case err := <- doneCh :
2021-08-27 12:16:36 -04:00
if err != nil {
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 1 } )
write ( [ ] byte ( err . Error ( ) ) )
2021-08-27 12:16:36 -04:00
} else {
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 0 } )
2021-08-27 12:16:36 -04:00
}
return
}
}
} ( )
return func ( err error ) {
if doneCh == nil {
return
}
// Indicate we are ready to write.
doneCh <- err
// Wait for channel to be closed so we don't race on writes.
<- doneCh
// Clear so we can be called multiple times without crashing.
doneCh = nil
} , & closeNotifier { rc : r . Body , done : bodyDoneCh }
}
2020-03-18 19:19:29 -04:00
// keepHTTPResponseAlive can be used to avoid timeouts with long storage
2021-02-26 18:11:42 -05:00
// operations, such as bitrot verification or data usage scanning.
2021-08-27 12:16:36 -04:00
// keepHTTPResponseAlive may NOT be used until the request body has been read,
// use keepHTTPReqResponseAlive instead.
2020-03-18 19:19:29 -04:00
// Every 10 seconds a space character is sent.
// The returned function should always be called to release resources.
2020-05-11 23:41:38 -04:00
// An optional error can be sent which will be picked as text only error,
// without its original type by the receiver.
2020-03-18 19:19:29 -04:00
// waitForHTTPResponse should be used to the receiving side.
2020-05-11 23:41:38 -04:00
func keepHTTPResponseAlive ( w http . ResponseWriter ) func ( error ) {
doneCh := make ( chan error )
2019-07-08 16:51:18 -04:00
go func ( ) {
2022-01-02 12:15:06 -05:00
canWrite := true
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write := func ( b [ ] byte ) {
if canWrite {
n , err := w . Write ( b )
if err != nil || n != len ( b ) {
canWrite = false
}
}
}
2024-01-28 13:04:17 -05:00
defer xioutil . SafeClose ( doneCh )
2019-07-08 16:51:18 -04:00
ticker := time . NewTicker ( time . Second * 10 )
2021-11-18 20:19:58 -05:00
defer ticker . Stop ( )
2019-07-08 16:51:18 -04:00
for {
select {
case <- ticker . C :
2024-04-28 13:53:50 -04:00
// The done() might have been called
// concurrently, check for it before we
// write the filler byte.
select {
case err := <- doneCh :
if err != nil {
write ( [ ] byte { 1 } )
write ( [ ] byte ( err . Error ( ) ) )
} else {
write ( [ ] byte { 0 } )
}
return
default :
}
2020-05-11 23:41:38 -04:00
// Response not ready, write a filler byte.
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 32 } )
if canWrite {
w . ( http . Flusher ) . Flush ( )
2021-11-18 20:19:58 -05:00
}
2020-05-11 23:41:38 -04:00
case err := <- doneCh :
if err != nil {
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 1 } )
write ( [ ] byte ( err . Error ( ) ) )
2020-05-11 23:41:38 -04:00
} else {
Fix panic (not fatal) on connection drops (#13811)
Fix more regressions from #13597 with double closed channels.
```
panic: "POST /minio/storage/data/distxl-plain/s1/d2/v42/createfile?disk-id=c789f7e1-2b52-442a-b518-aa2dac03f3a1&file-path=f6161668-b939-4543-9873-91b9da4cdff6%2F5eafa986-a3bf-4b1c-8bc0-03a37de390a3%2Fpart.1&length=2621760&volume=.minio.sys%2Ftmp": send on closed channel
goroutine 1977 [running]:
runtime/debug.Stack()
c:/go/src/runtime/debug/stack.go:24 +0x65
github.com/minio/minio/cmd.setCriticalErrorHandler.func1.1()
d:/minio/minio/cmd/generic-handlers.go:468 +0x8e
panic({0x2928860, 0x4fb17e0})
c:/go/src/runtime/panic.go:1038 +0x215
github.com/minio/minio/cmd.keepHTTPReqResponseAlive.func2({0x4fe4ea0, 0xc02737d8a0})
d:/minio/minio/cmd/storage-rest-server.go:818 +0x48
github.com/minio/minio/cmd.(*storageRESTServer).CreateFileHandler(0xc0015a8510, {0x50073e0, 0xc0273ec460}, 0xc029b9a400)
d:/minio/minio/cmd/storage-rest-server.go:334 +0x1d2
net/http.HandlerFunc.ServeHTTP(...)
c:/go/src/net/http/server.go:2046
github.com/minio/minio/cmd.httpTraceHdrs.func1({0x50073e0, 0xc0273ec460}, 0x0)
d:/minio/minio/cmd/handler-utils.go:372 +0x53
net/http.HandlerFunc.ServeHTTP(0x5007380, {0x50073e0, 0xc0273ec460}, 0x10)
c:/go/src/net/http/server.go:2046 +0x2f
github.com/minio/minio/cmd.addCustomHeaders.func1({0x5007380, 0xc0273dcf00}, 0xc0273f7340)
```
Reverts but adds write checks.
2021-12-02 14:22:32 -05:00
write ( [ ] byte { 0 } )
2020-05-11 23:41:38 -04:00
}
2019-07-08 16:51:18 -04:00
return
}
}
} ( )
2020-05-11 23:41:38 -04:00
return func ( err error ) {
if doneCh == nil {
return
}
2020-03-18 19:19:29 -04:00
// Indicate we are ready to write.
2020-05-11 23:41:38 -04:00
doneCh <- err
2020-03-18 19:19:29 -04:00
// Wait for channel to be closed so we don't race on writes.
<- doneCh
2020-05-11 23:41:38 -04:00
// Clear so we can be called multiple times without crashing.
doneCh = nil
2020-03-18 19:19:29 -04:00
}
}
// waitForHTTPResponse will wait for responses where keepHTTPResponseAlive
// has been used.
// The returned reader contains the payload.
func waitForHTTPResponse ( respBody io . Reader ) ( io . Reader , error ) {
reader := bufio . NewReader ( respBody )
for {
b , err := reader . ReadByte ( )
if err != nil {
return nil , err
}
2020-05-11 23:41:38 -04:00
// Check if we have a response ready or a filler byte.
switch b {
case 0 :
return reader , nil
case 1 :
2022-09-19 14:05:16 -04:00
errorText , err := io . ReadAll ( reader )
2020-05-11 23:41:38 -04:00
if err != nil {
return nil , err
2020-03-18 19:19:29 -04:00
}
2020-05-11 23:41:38 -04:00
return nil , errors . New ( string ( errorText ) )
case 32 :
continue
default :
return nil , fmt . Errorf ( "unexpected filler byte: %d" , b )
2020-03-18 19:19:29 -04:00
}
}
2019-07-08 16:51:18 -04:00
}
2020-10-28 12:18:35 -04:00
// httpStreamResponse allows streaming a response, but still send an error.
type httpStreamResponse struct {
done chan error
block chan [ ] byte
err error
}
2022-08-30 11:26:43 -04:00
// Write part of the streaming response.
2020-10-28 12:18:35 -04:00
// Note that upstream errors are currently not forwarded, but may be in the future.
func ( h * httpStreamResponse ) Write ( b [ ] byte ) ( int , error ) {
2020-11-11 21:07:40 -05:00
if len ( b ) == 0 || h . err != nil {
// Ignore 0 length blocks
return 0 , h . err
}
2020-10-28 12:18:35 -04:00
tmp := make ( [ ] byte , len ( b ) )
copy ( tmp , b )
h . block <- tmp
return len ( b ) , h . err
}
// CloseWithError will close the stream and return the specified error.
// This can be done several times, but only the first error will be sent.
// After calling this the stream should not be written to.
func ( h * httpStreamResponse ) CloseWithError ( err error ) {
if h . done == nil {
return
}
h . done <- err
h . err = err
// Indicates that the response is done.
<- h . done
h . done = nil
}
// streamHTTPResponse can be used to avoid timeouts with long storage
2021-02-26 18:11:42 -05:00
// operations, such as bitrot verification or data usage scanning.
2020-10-28 12:18:35 -04:00
// Every 10 seconds a space character is sent.
// The returned function should always be called to release resources.
// An optional error can be sent which will be picked as text only error,
// without its original type by the receiver.
// waitForHTTPStream should be used to the receiving side.
func streamHTTPResponse ( w http . ResponseWriter ) * httpStreamResponse {
doneCh := make ( chan error )
blockCh := make ( chan [ ] byte )
h := httpStreamResponse { done : doneCh , block : blockCh }
go func ( ) {
2022-01-02 12:15:06 -05:00
canWrite := true
2021-11-24 12:42:42 -05:00
write := func ( b [ ] byte ) {
if canWrite {
n , err := w . Write ( b )
if err != nil || n != len ( b ) {
canWrite = false
}
}
}
2020-10-28 12:18:35 -04:00
ticker := time . NewTicker ( time . Second * 10 )
2021-11-18 20:19:58 -05:00
defer ticker . Stop ( )
2020-10-28 12:18:35 -04:00
for {
select {
case <- ticker . C :
// Response not ready, write a filler byte.
2021-11-24 12:42:42 -05:00
write ( [ ] byte { 32 } )
if canWrite {
w . ( http . Flusher ) . Flush ( )
2021-11-18 20:19:58 -05:00
}
2020-10-28 12:18:35 -04:00
case err := <- doneCh :
if err != nil {
2021-11-24 12:42:42 -05:00
write ( [ ] byte { 1 } )
write ( [ ] byte ( err . Error ( ) ) )
2020-10-28 12:18:35 -04:00
} else {
2021-11-24 12:42:42 -05:00
write ( [ ] byte { 0 } )
2020-10-28 12:18:35 -04:00
}
2024-01-28 13:04:17 -05:00
xioutil . SafeClose ( doneCh )
2020-10-28 12:18:35 -04:00
return
case block := <- blockCh :
var tmp [ 5 ] byte
tmp [ 0 ] = 2
binary . LittleEndian . PutUint32 ( tmp [ 1 : ] , uint32 ( len ( block ) ) )
2021-11-24 12:42:42 -05:00
write ( tmp [ : ] )
write ( block )
if canWrite {
w . ( http . Flusher ) . Flush ( )
2021-11-18 20:19:58 -05:00
}
2020-10-28 12:18:35 -04:00
}
}
} ( )
return & h
}
2021-11-02 11:11:50 -04:00
var poolBuf8k = sync . Pool {
New : func ( ) interface { } {
b := make ( [ ] byte , 8192 )
return & b
} ,
}
2022-10-18 16:50:21 -04:00
var poolBuf128k = sync . Pool {
New : func ( ) interface { } {
b := make ( [ ] byte , 128 << 10 )
return b
} ,
}
2020-10-28 12:18:35 -04:00
// waitForHTTPStream will wait for responses where
// streamHTTPResponse has been used.
// The returned reader contains the payload and must be closed if no error is returned.
func waitForHTTPStream ( respBody io . ReadCloser , w io . Writer ) error {
var tmp [ 1 ] byte
2021-05-07 12:11:05 -04:00
// 8K copy buffer, reused for less allocs...
2021-11-02 11:11:50 -04:00
bufp := poolBuf8k . Get ( ) . ( * [ ] byte )
buf := * bufp
defer poolBuf8k . Put ( bufp )
2020-10-28 12:18:35 -04:00
for {
_ , err := io . ReadFull ( respBody , tmp [ : ] )
if err != nil {
return err
}
// Check if we have a response ready or a filler byte.
switch tmp [ 0 ] {
case 0 :
// 0 is unbuffered, copy the rest.
2021-11-02 11:11:50 -04:00
_ , err := io . CopyBuffer ( w , respBody , buf )
2020-10-28 12:18:35 -04:00
if err == io . EOF {
return nil
}
return err
case 1 :
2022-09-19 14:05:16 -04:00
errorText , err := io . ReadAll ( respBody )
2020-10-28 12:18:35 -04:00
if err != nil {
return err
}
return errors . New ( string ( errorText ) )
case 2 :
// Block of data
var tmp [ 4 ] byte
_ , err := io . ReadFull ( respBody , tmp [ : ] )
if err != nil {
return err
}
length := binary . LittleEndian . Uint32 ( tmp [ : ] )
2022-06-02 12:16:26 -04:00
n , err := io . CopyBuffer ( w , io . LimitReader ( respBody , int64 ( length ) ) , buf )
2020-10-28 12:18:35 -04:00
if err != nil {
return err
}
2022-06-02 12:16:26 -04:00
if n != int64 ( length ) {
return io . ErrUnexpectedEOF
}
2020-10-28 12:18:35 -04:00
continue
case 32 :
continue
default :
return fmt . Errorf ( "unexpected filler byte: %d" , tmp [ 0 ] )
}
}
}
2020-06-12 23:04:01 -04:00
// VerifyFileHandler - Verify all part of file for bitrot errors.
func ( s * storageRESTServer ) VerifyFileHandler ( w http . ResponseWriter , r * http . Request ) {
2019-07-08 16:51:18 -04:00
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
2020-06-12 23:04:01 -04:00
if r . ContentLength < 0 {
s . writeErrorResponse ( w , errInvalidArgument )
2019-07-12 19:29:44 -04:00
return
}
2020-06-12 23:04:01 -04:00
var fi FileInfo
2020-11-02 20:07:52 -05:00
if err := msgp . Decode ( r . Body , & fi ) ; err != nil {
2019-07-08 16:51:18 -04:00
s . writeErrorResponse ( w , err )
return
}
2020-06-12 23:04:01 -04:00
2020-07-30 22:45:12 -04:00
setEventStreamHeaders ( w )
2019-10-01 16:12:15 -04:00
encoder := gob . NewEncoder ( w )
2020-03-18 19:19:29 -04:00
done := keepHTTPResponseAlive ( w )
2024-06-10 11:51:27 -04:00
resp , err := s . getStorage ( ) . VerifyFile ( r . Context ( ) , volume , filePath , fi )
2020-05-11 23:41:38 -04:00
done ( nil )
2024-06-10 11:51:27 -04:00
2019-10-01 16:12:15 -04:00
if err != nil {
2024-06-10 11:51:27 -04:00
s . writeErrorResponse ( w , err )
return
2019-10-01 16:12:15 -04:00
}
2024-06-10 11:51:27 -04:00
encoder . Encode ( resp )
2019-07-08 16:51:18 -04:00
}
2021-12-03 12:25:17 -05:00
func checkDiskFatalErrs ( errs [ ] error ) error {
// This returns a common error if all errors are
// same errors, then there is no point starting
// the server.
if countErrs ( errs , errUnsupportedDisk ) == len ( errs ) {
return errUnsupportedDisk
}
if countErrs ( errs , errDiskAccessDenied ) == len ( errs ) {
return errDiskAccessDenied
}
if countErrs ( errs , errFileAccessDenied ) == len ( errs ) {
return errDiskAccessDenied
}
if countErrs ( errs , errDiskNotDir ) == len ( errs ) {
return errDiskNotDir
}
if countErrs ( errs , errFaultyDisk ) == len ( errs ) {
return errFaultyDisk
}
2022-05-30 13:58:37 -04:00
if countErrs ( errs , errXLBackend ) == len ( errs ) {
return errXLBackend
}
2021-12-03 12:25:17 -05:00
return nil
}
2020-09-28 22:39:32 -04:00
// A single function to write certain errors to be fatal
// or informative based on the `exit` flag, please look
// at each implementation of error for added hints.
//
// FIXME: This is an unusual function but serves its purpose for
2024-01-18 02:03:17 -05:00
// now, need to revisit the overall erroring structure here.
2020-09-28 22:39:32 -04:00
// Do not like it :-(
func logFatalErrs ( err error , endpoint Endpoint , exit bool ) {
2021-05-15 15:56:58 -04:00
switch {
2022-05-30 13:58:37 -04:00
case errors . Is ( err , errXLBackend ) :
logger . Fatal ( config . ErrInvalidXLValue ( err ) , "Unable to initialize backend" )
2021-05-15 15:56:58 -04:00
case errors . Is ( err , errUnsupportedDisk ) :
2020-09-28 22:39:32 -04:00
var hint string
if endpoint . URL != nil {
2022-08-04 19:10:08 -04:00
hint = fmt . Sprintf ( "Drive '%s' does not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support" , endpoint . Path )
2020-09-28 22:39:32 -04:00
} else {
2022-08-04 19:10:08 -04:00
hint = "Drives do not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support"
2020-09-28 22:39:32 -04:00
}
logger . Fatal ( config . ErrUnsupportedBackend ( err ) . Hint ( hint ) , "Unable to initialize backend" )
2021-05-15 15:56:58 -04:00
case errors . Is ( err , errDiskNotDir ) :
2020-09-28 22:39:32 -04:00
var hint string
if endpoint . URL != nil {
2022-08-04 19:10:08 -04:00
hint = fmt . Sprintf ( "Drive '%s' is not a directory, MinIO erasure coding needs a directory" , endpoint . Path )
2020-09-28 22:39:32 -04:00
} else {
2022-08-04 19:10:08 -04:00
hint = "Drives are not directories, MinIO erasure coding needs directories"
2020-09-28 22:39:32 -04:00
}
logger . Fatal ( config . ErrUnableToWriteInBackend ( err ) . Hint ( hint ) , "Unable to initialize backend" )
2021-12-03 12:25:17 -05:00
case errors . Is ( err , errDiskAccessDenied ) :
2020-09-28 22:39:32 -04:00
// Show a descriptive error with a hint about how to fix it.
var username string
if u , err := user . Current ( ) ; err == nil {
username = u . Username
} else {
username = "<your-username>"
}
var hint string
if endpoint . URL != nil {
hint = fmt . Sprintf ( "Run the following command to add write permissions: `sudo chown -R %s %s && sudo chmod u+rxw %s`" ,
username , endpoint . Path , endpoint . Path )
} else {
hint = fmt . Sprintf ( "Run the following command to add write permissions: `sudo chown -R %s. <path> && sudo chmod u+rxw <path>`" , username )
}
2021-05-15 15:56:58 -04:00
if ! exit {
2024-04-04 08:04:40 -04:00
storageLogOnceIf ( GlobalContext , fmt . Errorf ( "Drive is not writable %s, %s" , endpoint , hint ) , "log-fatal-errs" )
2021-05-15 15:56:58 -04:00
} else {
logger . Fatal ( config . ErrUnableToWriteInBackend ( err ) . Hint ( hint ) , "Unable to initialize backend" )
}
case errors . Is ( err , errFaultyDisk ) :
2020-09-28 22:39:32 -04:00
if ! exit {
2024-04-04 08:04:40 -04:00
storageLogOnceIf ( GlobalContext , fmt . Errorf ( "Drive is faulty at %s, please replace the drive - drive will be offline" , endpoint ) , "log-fatal-errs" )
2020-09-28 22:39:32 -04:00
} else {
logger . Fatal ( err , "Unable to initialize backend" )
}
2021-05-15 15:56:58 -04:00
case errors . Is ( err , errDiskFull ) :
2020-09-28 22:39:32 -04:00
if ! exit {
2024-04-04 08:04:40 -04:00
storageLogOnceIf ( GlobalContext , fmt . Errorf ( "Drive is already full at %s, incoming I/O will fail - drive will be offline" , endpoint ) , "log-fatal-errs" )
2020-09-28 22:39:32 -04:00
} else {
logger . Fatal ( err , "Unable to initialize backend" )
}
2024-04-11 13:45:28 -04:00
case errors . Is ( err , errInconsistentDisk ) :
if exit {
logger . Fatal ( err , "Unable to initialize backend" )
}
2021-05-15 15:56:58 -04:00
default :
2020-09-28 22:39:32 -04:00
if ! exit {
2024-04-04 08:04:40 -04:00
storageLogOnceIf ( GlobalContext , fmt . Errorf ( "Drive %s returned an unexpected error: %w, please investigate - drive will be offline" , endpoint , err ) , "log-fatal-errs" )
2020-09-28 22:39:32 -04:00
} else {
logger . Fatal ( err , "Unable to initialize backend" )
}
}
}
2021-07-09 14:29:16 -04:00
// StatInfoFile returns file stat info.
func ( s * storageRESTServer ) StatInfoFile ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
2021-12-09 11:38:46 -05:00
volume := r . Form . Get ( storageRESTVolume )
filePath := r . Form . Get ( storageRESTFilePath )
glob := r . Form . Get ( storageRESTGlob )
2021-07-09 14:29:16 -04:00
done := keepHTTPResponseAlive ( w )
2023-12-01 15:01:14 -05:00
stats , err := s . getStorage ( ) . StatInfoFile ( r . Context ( ) , volume , filePath , glob == "true" )
2021-07-09 14:29:16 -04:00
done ( err )
if err != nil {
return
}
2021-10-01 14:50:00 -04:00
for _ , si := range stats {
msgp . Encode ( w , & si )
}
2021-07-09 14:29:16 -04:00
}
2022-07-19 11:35:29 -04:00
// ReadMultiple returns multiple files
func ( s * storageRESTServer ) ReadMultiple ( w http . ResponseWriter , r * http . Request ) {
if ! s . IsValid ( w , r ) {
return
}
rw := streamHTTPResponse ( w )
defer func ( ) {
if r := recover ( ) ; r != nil {
debug . PrintStack ( )
rw . CloseWithError ( fmt . Errorf ( "panic: %v" , r ) )
}
} ( )
var req ReadMultipleReq
mr := msgpNewReader ( r . Body )
2023-07-06 19:02:08 -04:00
defer readMsgpReaderPoolPut ( mr )
2022-07-19 11:35:29 -04:00
err := req . DecodeMsg ( mr )
if err != nil {
rw . CloseWithError ( err )
return
}
mw := msgp . NewWriter ( rw )
responses := make ( chan ReadMultipleResp , len ( req . Files ) )
var wg sync . WaitGroup
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
for resp := range responses {
err := resp . EncodeMsg ( mw )
if err != nil {
rw . CloseWithError ( err )
return
}
mw . Flush ( )
}
} ( )
2023-12-01 15:01:14 -05:00
err = s . getStorage ( ) . ReadMultiple ( r . Context ( ) , req , responses )
2022-07-19 11:35:29 -04:00
wg . Wait ( )
rw . CloseWithError ( err )
}
2023-12-13 22:27:55 -05:00
// globalLocalSetDrives is used for local drive as well as remote REST
// API caller for other nodes to talk to this node.
//
// Any updates to this must be serialized via globalLocalDrivesMu (locker)
var globalLocalSetDrives [ ] [ ] [ ] StorageAPI
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
// registerStorageRESTHandlers - register storage rpc router.
func registerStorageRESTHandlers ( router * mux . Router , endpointServerPools EndpointServerPools , gm * grid . Manager ) {
2023-07-08 10:35:11 -04:00
h := func ( f http . HandlerFunc ) http . HandlerFunc {
return collectInternodeStats ( httpTraceHdrs ( f ) )
}
2023-12-13 22:27:55 -05:00
globalLocalSetDrives = make ( [ ] [ ] [ ] StorageAPI , len ( endpointServerPools ) )
for pool := range globalLocalSetDrives {
globalLocalSetDrives [ pool ] = make ( [ ] [ ] StorageAPI , endpointServerPools [ pool ] . SetCount )
for set := range globalLocalSetDrives [ pool ] {
globalLocalSetDrives [ pool ] [ set ] = make ( [ ] StorageAPI , endpointServerPools [ pool ] . DrivesPerSet )
}
2023-12-01 15:01:14 -05:00
}
2023-12-13 22:27:55 -05:00
for _ , serverPool := range endpointServerPools {
for _ , endpoint := range serverPool . Endpoints {
2023-12-01 15:01:14 -05:00
if ! endpoint . IsLocal {
2021-05-18 20:25:00 -04:00
continue
2019-11-19 20:42:27 -05:00
}
2023-12-04 14:54:13 -05:00
2023-12-13 22:27:55 -05:00
server := & storageRESTServer {
2024-02-28 02:02:14 -05:00
endpoint : endpoint ,
2023-12-13 22:27:55 -05:00
}
2019-11-19 20:42:27 -05:00
subrouter := router . PathPrefix ( path . Join ( storageRESTPrefix , endpoint . Path ) ) . Subrouter ( )
2023-07-08 10:35:11 -04:00
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodHealth ) . HandlerFunc ( h ( server . HealthHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodAppendFile ) . HandlerFunc ( h ( server . AppendFileHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodReadVersion ) . HandlerFunc ( h ( server . ReadVersionHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodReadXL ) . HandlerFunc ( h ( server . ReadXLHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodCreateFile ) . HandlerFunc ( h ( server . CreateFileHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodReadFile ) . HandlerFunc ( h ( server . ReadFileHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodReadFileStream ) . HandlerFunc ( h ( server . ReadFileStreamHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodDeleteVersions ) . HandlerFunc ( h ( server . DeleteVersionsHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodVerifyFile ) . HandlerFunc ( h ( server . VerifyFileHandler ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodStatInfoFile ) . HandlerFunc ( h ( server . StatInfoFile ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodReadMultiple ) . HandlerFunc ( h ( server . ReadMultiple ) )
subrouter . Methods ( http . MethodPost ) . Path ( storageRESTVersionPrefix + storageRESTMethodCleanAbandoned ) . HandlerFunc ( h ( server . CleanAbandonedDataHandler ) )
2024-02-19 17:54:46 -05:00
logger . FatalIf ( storageListDirRPC . RegisterNoInput ( gm , server . ListDirHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageReadAllRPC . Register ( gm , server . ReadAllHandler , endpoint . Path ) , "unable to register handler" )
2024-02-28 12:54:52 -05:00
logger . FatalIf ( storageWriteAllRPC . Register ( gm , server . WriteAllHandler , endpoint . Path ) , "unable to register handler" )
2024-02-19 17:54:46 -05:00
logger . FatalIf ( storageRenameFileRPC . Register ( gm , server . RenameFileHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageRenameDataRPC . Register ( gm , server . RenameDataHandler , endpoint . Path ) , "unable to register handler" )
2024-04-01 19:42:09 -04:00
logger . FatalIf ( storageRenameDataInlineRPC . Register ( gm , server . RenameDataInlineHandler , endpoint . Path ) , "unable to register handler" )
2024-02-19 17:54:46 -05:00
logger . FatalIf ( storageDeleteFileRPC . Register ( gm , server . DeleteFileHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageCheckPartsRPC . Register ( gm , server . CheckPartsHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageReadVersionRPC . Register ( gm , server . ReadVersionHandlerWS , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageWriteMetadataRPC . Register ( gm , server . WriteMetadataHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageUpdateMetadataRPC . Register ( gm , server . UpdateMetadataHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageDeleteVersionRPC . Register ( gm , server . DeleteVersionHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageReadXLRPC . Register ( gm , server . ReadXLHandlerWS , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageNSScannerRPC . RegisterNoInput ( gm , server . NSScannerHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageDiskInfoRPC . Register ( gm , server . DiskInfoHandler , endpoint . Path ) , "unable to register handler" )
logger . FatalIf ( storageStatVolRPC . Register ( gm , server . StatVolHandler , endpoint . Path ) , "unable to register handler" )
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
logger . FatalIf ( gm . RegisterStreamingHandler ( grid . HandlerWalkDir , grid . StreamHandler {
Subroute : endpoint . Path ,
Handle : server . WalkDirHandler ,
OutCapacity : 1 ,
} ) , "unable to register handler" )
2023-12-01 15:01:14 -05:00
2024-05-09 14:03:54 -04:00
createStorage := func ( endpoint Endpoint ) bool {
2023-12-01 15:01:14 -05:00
xl , err := newXLStorage ( endpoint , false )
if err != nil {
// if supported errors don't fail, we proceed to
// printing message and moving forward.
2024-03-03 02:45:39 -05:00
if errors . Is ( err , errDriveIsRoot ) {
err = fmt . Errorf ( "major: %v: minor: %v: %w" , xl . major , xl . minor , err )
}
2023-12-01 15:01:14 -05:00
logFatalErrs ( err , endpoint , false )
return false
}
storage := newXLStorageDiskIDCheck ( xl , true )
storage . SetDiskID ( xl . diskID )
2024-01-23 17:11:46 -05:00
// We do not have to do SetFormatData() since 'xl'
// already captures formatData cached.
2023-12-13 22:27:55 -05:00
globalLocalDrivesMu . Lock ( )
defer globalLocalDrivesMu . Unlock ( )
globalLocalDrives = append ( globalLocalDrives , storage )
2024-05-22 19:07:14 -04:00
globalLocalDrivesMap [ endpoint . String ( ) ] = storage
2023-12-13 22:27:55 -05:00
globalLocalSetDrives [ endpoint . PoolIdx ] [ endpoint . SetIdx ] [ endpoint . DiskIdx ] = storage
2023-12-01 15:01:14 -05:00
return true
}
2023-12-13 22:27:55 -05:00
2024-05-09 14:03:54 -04:00
if createStorage ( endpoint ) {
2023-12-01 15:01:14 -05:00
continue
}
2023-12-13 22:27:55 -05:00
2023-12-01 15:01:14 -05:00
// Start async goroutine to create storage.
2024-05-09 14:03:54 -04:00
go func ( endpoint Endpoint ) {
2023-12-01 15:01:14 -05:00
for {
2023-12-13 22:27:55 -05:00
time . Sleep ( 3 * time . Second )
2024-05-09 14:03:54 -04:00
if createStorage ( endpoint ) {
2023-12-01 15:01:14 -05:00
return
}
}
2024-05-09 14:03:54 -04:00
} ( endpoint )
2023-12-04 14:54:13 -05:00
2023-12-01 15:01:14 -05:00
}
2023-12-01 03:18:04 -05:00
}
2018-10-04 20:44:06 -04:00
}