Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add migration guide for libp2p@0.46.x #1883

Merged
merged 7 commits into from
Jul 28, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions doc/migrations/v0.45-v0.46.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Migrating to libp2p@46 <!-- omit in toc -->

A migration guide for refactoring your application code from libp2p v__ to v__.
achingbrain marked this conversation as resolved.
Show resolved Hide resolved

## Table of Contents <!-- omit in toc -->

- [Graceful stream closing](#graceful-stream-closing)
- [Stream/Connection stat properties](#streamconnection-stat-properties)
- [Interface module consolidation](#interface-module-consolidation)

## Graceful stream closing

Streams can either be closed gracefully, where we wait for any unsent data to be sent, or aborted in which case any unsent data is discarded and a reset message is sent, notifying the remote of the abnormal termination.
maschad marked this conversation as resolved.
Show resolved Hide resolved

To close a stream gracefully we call the `.close` method (or `.closeRead`/`.closeWrite` for when we want half-closed streams). To abort a stream we call `.abort` and pass an error object.

In previous versions the `.close` method was synchronous which meant it could not wait for existing data to be sent which made nodes behave unpredictably.

From `0.46.x` the `.close`/`.closeRead`/`.closeWrite` methods on the Stream interface are now asynchronous. `.abort` is a synchronous method that accepts an Error object.

Similarly the Connection interface now has asynchronous `.close` and synchronous `.abort` methods.

The `.reset` method has been removed from the Stream interface as it is only to be invoked internally by stream multiplexers when a remote stream reset has occurred.

**Before**

```js
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going forward we should really utilize Typescript examples so that they can be compiled and thus reduce the probability of doc errors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, though it'll involve a lot of boilerplate imports.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but otherwise we lose the benefit of the type safety. Perhaps we can begin with #1777 as a precedent.

const stream = await libp2p.dialProtocol(multiaddr, '/my-protocol/1.0.0')

// send some data
await stream.sink([data])

// close the stream - previously this may not have waited for the data to be sent
stream.close()

// alternatively cause the stream to error on the remote
stream.abort(new Error('Oh no!'))
```

**After**

```js
const stream = await libp2p.dialProtocol(multiaddr, '/my-protocol/1.0.0')

// send some data
await stream.sink([data])

// close the stream - this method is now async
await stream.close()

// alternatively cause the stream to error on the remote
stream.abort(new Error('Oh no!'))
```

## Stream/Connection stat properties

The properties on the `stream.stat` and `connection.stat` objects are now stored on the stream/connection itself.

**Before**

```js
// stream.stat properties
console.info(stream.stat.direction)
console.info(stream.stat.timeline)
console.info(stream.stat.protocol)

// connection.stat properties
console.info(connection.stat.direction)
console.info(connection.stat.timeline)
console.info(connection.stat.multiplexer)
console.info(connection.stat.encryption)
console.info(connection.stat.status)
```

**After**

```js
// stream.stat properties
console.info(stream.direction)
console.info(stream.timeline)
console.info(stream.protocol)

// connection.stat properties
console.info(connection.direction)
console.info(connection.timeline)
console.info(connection.multiplexer)
console.info(connection.encryption)
console.info(connection.status)
```

## Interface module consolidation

In an effort to prevent breaking changes affecting unrelated modules, libp2p prior to 0.46.x had a large number of single-issue interface modules for internal and external types - `@libp2p/address-manager`, `@libp2p/connection-gater`, `@libp2p/connection-manager` and so on.

This meant that although we could release a new version of the address manager interface without impacting modules that only depended on the connection manager, releasing any change became a multiple-step process during which there was a time window sometimes lasting several days when the latest versions of modules would be incompatible with each other.

Adding new methods and types to interfaces also became a breaking change since the existing released implementations of those interfaces would not implement the new methods which complicated matters further.

Since [libp2p/js-libp2p#1792](https://github.com/libp2p/js-libp2p/pull/1792) converted libp2p into a monorepo project, a lot of these problems have gone away since we can now release multiple libp2p modules simultaneously.

The urgency that required multiple interface modules has also subsided somewhat so now all libp2p interfaces are collected into two modules - `@lib2p2p/interface` for public-facing APIs and `@libp2p/interface-internal` for APIs designed to be consumed by libp2p components.

**Before**

```js
import type { Libp2p } from '@libp2p/interface-libp2p'
import type { AddressManager } from '@libp2p/interface-address-manager'
import type { ConnectionManager } from '@libp2p/interface-connection-manager'
// etc
```

**After**

```js
import type { Libp2p } from '@libp2p/interface'
import type { AddressManager } from '@libp2p/interface-internal/address-manager'
import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
// etc
```
Loading