土曜日, 12月 20, 2025
No menu items!
ホーム ブログ ページ 4760

「Whizzerが8周年!4種切替イヤホン登場」

伊藤屋国際、フラグシップイヤホン「Kylin WCI53」を発表

新たなチューニング切り替え機能搭載

伊藤屋国際は、Whizzerブランドから新たなフラグシップイヤホン「Kylin WCI53」を投入することを発表しました。この製品は、特にチューニング切り替え機能を搭載しており、ユーザーは好みに応じた音質を選ぶことができます。

発売情報と価格

「Kylin WCI53」は、2025年6月27日(金)より発売され、価格は88,000円(税込)となっています。この価格帯は、品質や機能性を求めるオーディオ愛好家にとって魅力的です。

重要なポイント

  • チューニング切り替え機能: 異なる音質モードを切り替えることが可能で、さまざまな音楽ジャンルに対応します。
  • 高品質な音響体験: Whizzerブランドの特性を活かし、クリアで豊かな音質を提供。

この新製品は、オーディオファンだけでなく、日常的に音楽を楽しむすべての人々にとって魅力的な選択肢となりそうです。

詳細情報や購入方法については、伊藤屋国際の公式ウェブサイトをご覧ください。


これで、「Kylin WCI53」の特徴や発売情報を押さえながら、読者にもわかりやすく紹介しました。

🧠 編集部より:

補足説明

伊藤屋国際が展開するWhizzerブランドから、新たに登場するフラグシップイヤホン「Kylin WCI53」は、チューニング切り替え機能を搭載しています。この機能により、ユーザーは好みに応じて音質を調整することができ、より個性的なリスニング体験を楽しむことが可能です。発売日は2023年6月27日(金)、価格は88,000円(税込)となっています。

背景や豆知識

Whizzerは、高品質な音楽体験を提供するための先進的な技術とデザインに定評があります。特に、チューニング切り替え機能は、自分の好みに合わせた音質を素早く切り替えられるため、さまざまな音楽ジャンルに適応できます。たとえば、クラシック音楽にはクリアな高音が求められ、ポップスの場合にはバランスの取れた音域が好まれることが多いです。

関連リンク

もっとWhizzerブランドの情報や製品を確認したい方は、こちらの公式サイトをチェックしてみてください:Whizzer Official

  • キーワード: Kylin WCI53

Kylin WCI53 をAmazonで探す
Whizzerブランド をAmazonで探す
イヤホン をAmazonで探す



※以下、出典元
▶ 元記事を読む

Views: 0

おっしゃ Let’s 大規模リリースだ! #心構え – Qiita



おっしゃ Let's 大規模リリースだ! #心構え - Qiita

事業部MVPを取った優秀な後輩から「イベントだし、おまえも記事書くよな?」的な圧を感じたので記事を書いたのですが、「まだまだ書けるよな?」と新しい圧を感じたので、もう一記事書くことにしました。

大規模リリースって、意外とあるよね

プロダクト開発をしていると、ちょっと大きめのリリースってありますよね。
本当は細かくリリースしていきたいけど、いろんな事情で大きくリリースすることになったりしますよね。

大規模リリースは戦?祭り?オーケストラ?

大規模リリース と聞いた時、どんなイメージを持ちますか?
戦?祭り?オーケストラ?
人それぞれ印象が違うと思います。
その違いは、それまでの過去の経験によって左右されている気がします。しらんけど。

私にとっての大規模リリースはなんだろう?
祭りに近いかもしれません。たぶん。

この記事で書くこと

大規模リリースで気をつけたいこと、気をつけていることをまとめてみようと思います。

ぱみゅぱみゅ先生の歌詞をオマージュしました。
なんとなく。なんとなくです。
ただ言ってみたかっただけです。

ここから先は真面目に書きます。

1. テスト期間 – 不具合ゼロ って不安じゃない?

「よし!今回は完璧だ!テストで不具合も見つからない!」

これはもう完全に個人的な経験則ですが、 テストで不具合が見つからないときほど不安になることはない です。
だって、大規模リリースですよ?
多くの新機能だったり、多くの改修だったり、リリース対象が多いんです。
そんな状況で不具合ゼロって、 テストが機能していなくて絶対何かを見落としている可能性の方が高い と考えてしまいます。
不具合が出すぎても不安ですが、不具合ゼロの方が圧倒的に不安になります。

2. リリース準備 – 「リリース手順書は猿でも分かるように書け!」

「自分でリリース作業するし、イメージできてるから、手順書とか省いちゃっていいか!」

SIerにいた頃、上司から口酸っぱく言われていました。
「もしお前が体調崩したり急遽休まなきゃいけなくなったらどうするつもり?そこまで考えて準備するのがプロの仕事だぞ?」

猿でも分かる手順書って、どんな手順書?

手順書作成や、手順書のレビューをする際に気をつけている点は、こんな感じです。

  • 手順は とにかく細かく
    • CUI操作があるときは、手順書にコマンドを省略せずに書き、そのままコピペできるようにする
    • GUI操作があるときは、〇〇の画面で□□というボタンを押下するといったレベルで書く
  • 完了条件を 明示 する
    • 手順を実行した結果、どうなっていたら次のステップに移れるか明確に書く
  • リトライ判断や切り戻し 基準も明確 にする
    • 初めて作業する人でも判断に迷わない基準がBest
    • 例えば、このような感じです。
      • ここまで行って、△△となっていない場合は、N番の手順をリトライする
      • このメッセージが表示されたら、リリース作業を中止して切り戻し手順のXを実行する

リリースのリハーサルをやるなら、できる限り本番と同じ状況で実施

リリースリハを行う場合、できる限り本番と同じ状況で確認しましょう。
本番リリースが平日午前中なら平日午前中に。
週末の深夜帯なら週末の深夜帯に。

バッチ処理とぶつかって、作業が止まってしまう。
その時間帯はネットワーク負荷が高くて、リモート操作がスムーズに行かない。
そんな 予期せぬ事態 が潜んでいるかもしれないです。
なので、できるだけ本番と同じ状況でのリハーサルが吉です。

3. リリース作業 – 勝手なことをするな!

「手順書を何回も見たし、覚えているから安心!」

せっかく丁寧に猿でも分かるような手順書を書いて、それに沿ったリハーサルをしたのですから、手順書通りに作業しましょう。
「こっちのコマンドの方が効果的だから!」
「こっちの画面から操作しちゃおう!」
といった、その場の判断はやめましょう。

手順書は、 料理のレシピ のようなものです。
小さじを計らず目分量にしたり、手順をすっ飛ばしたり、勝手にアレンジを加えて、料理が台無しになった経験ありませんか?
リリース作業も同じです。

冷静に、丁寧に、レシピ(手順書)通りに。

4. リリース後の臨戦態勢 – リリース後は心もスケジュールも余裕を持って!

「よし、無事にリリースも終わったし、次の開発スタートしちゃうか!」

いくら本番同等のステージング環境でテストを行っても、
いくら本番同様の状況でリハーサルを行っても、
完璧に同じ状況ではないので、何かが起こるかもしれません。

そんな時、だれも気付けなかったら?
そんな時、だれも対応する余裕がなかったら?

考えるだけでもゾッとしますよね。

リリース後は 心に余裕 を!
リリース後は スケジュールに余裕 を!
もし仮に何かがあっても即座に対応できる余力を作っておきましょう。

個人的には、システムステータスとかアラートチャンネルを横目に雑談してるくらいでちょうど良いと思っています。
(まぁ、そこまでの余裕は難しい場合がほとんどですが・・・)

大前提として、避けられるのであれば大規模なリリースは避けたいっていうのが本音ではあります。
それでも、様々な事情で避けられないこともあります。

リリースは、祭りかもしれないし、戦いかもしれないし、オーケストラかもしれません。
でも、どんな形であっても、「良いリリースだった」と言える日になるよう、冷静に、丁寧に、そして少しの余白をもって臨んでいきたいですね。

最後に – 私たちの最近のリリース

私もつい先日、PdMを担当しているプロダクトで大きめのリリースを行いました。
別プロダクトとの連携のあるリリースで、事業部MVPを取っちゃうような優秀な後輩の所属する部署のみんなとリリース前日まで調整と確認を繰り返していましたが、無事にリリースが完了しました。
後輩もその部署のみんなも全員優秀で全員が協力的でとても助かりました。助けられました。本当にありがとうございました。





Source link

Views: 0

Effect.tsはストリームを扱う目的でも便利だよ、という話 with BigQuery Storage Write API



この記事の目標

クオータを越えないようにしつつ、BigQueryにStorage Write APIでインサートしてみよう。ということになります。
この記事を読むにあたって、BigQueryの知識は必要ないです。

詳しくはBigQuery Storage Write API の概要 – Google Cloudを参照。
BigQueryはビッグデータ向けのデータベース、分析ツールです。
ストリームで処理しつつ、ストリーム単位のトランザクションが扱えたり、重複して挿入されない保証をしたりできます。
たとえば、旧来のやつは二重挿入されることもありえたりしました。

こちらを利用して、以下のことを実現したいとします。

  • Stream.Stream を受けとり、すべてを挿入するか、適切に失敗する。
  • append-rows APIで分割して送りたい。 (append-rowsは AppendRowsInputItem[] を取ると思ってもらってよい)
  • append-rowsは一回あたり10MBまでで送らなければならないので、その制約を守りたい。
  • append-rowsは未処理のもの(Promiseが帰ってきてないもの)のサイズ合計が10MBまででなければならないので、その制約を守りたい。
BigQueryユーザ向け詳細

BigQuery Storage Write APIはBigQueryの次世代APIのようなもので、ストリーム処理をトランザクショナルに扱ういくつかの新しいAPIが提供されていたりします。
BigQuery Storage Write APIのベストプラクティスにはappend-rowsは待たずに突っこみまくっていい、というようなことが書かれているんですが、実際にそれをやると10MBの制限が存在し、未処理のものが10MB溜まる瞬間がないようにしなければいけないです。
(リクエスト一個ずつが10MB以内になるだけでは不十分)
ドキュメントに明示している箇所がみあたらなかったのですが、実際小分けにして送りまくってみると、エラーでそのように言われます。(ドキュメントに書いてほしい。)

Effect.tsで必要な道具

Effect.tsで必要な道具を紹介していきます。

StreamとChunk

import { Stream, Chunk } from 'effect';
  • Stream.StreamT のストリームを表します。
  • 以下のように他のものからStreamを作れます。
    • Stream.fromAsyncIterable: AsyncIterable から帰着できます。
    • Stream.fromReadableStream: WebAPIの ReadableStream から帰着できます。
  • 内部的にはチャンク(Chunk.Chunk)を持ちますが、明示的に露出させなければ、それが見られることはありません。
    • Stream.chunks すると Stream.Stream> が得られ、内部のチャンクが露出したストリームになります。
    • チャンクはいい感じにしてくれるものではなく、どのようにするか、ということは何らかの方法でどこかで与えなければなりません。
      • いい感じにする、というのを作ってかませることはできます。
    • Stream.Stream はつまり、(内部チャンクに意味を持たせている文脈においては)ある程度、いい感じの単位にまとまっている状態で、しかしそれを透過的に扱う、という目的に利用できます。
    • 逆に、いい感じになってるのだろうな、ではなく、チャンクそのものに明確に強い意味を持たせるなら、 Stream.Stream> を使いたいということになるでしょう。
    • 大抵の最初に生まれるストリームの内部チャンクは、1要素を1個ずつ括るチャンクになっていると思って良いでしょう。
      • これはつまり、なにもしてないのと同等で、なにかしたいなら自分でしなければならないことを意味します。
  • 配列の .map 等のようなものが、より広く揃っています: 代表的には、 Stream.filter, Stream.map, Stream.take, Stream.flatMap, などなど。
  • Stream vs Chunk
    • StreamはそのままAsyncIterableだと思っておけば良いです。
    • Chunkは、いわゆる普通に配列みたいに思って良いですが、よりarray構造体だと思ってよいしそのように最適化されている対象だと見るとよさそうです。
      • append(要素追加)などはネイティブ配列より速いらしい。
      • effectにはListというのが別であるので、List vs Chunkで考えるのがよりよいかと思います。いわゆる リンクリスト vs 可変長配列 の関係にあたります。
        • C++ でいえば std::liststd::vector の関係
        • Rust でいえば LinkedListVec の関係
        • データ構造をやろう。
      • effectにはArrayというのも生えていますが、これはネイティブ配列を扱うためのもので、特別、新しい構造体ではないです。
  • Effectの一員なので、途中でどのようなエラーが起こり得るか、どのような副作用が必要か、が型に表れます。
    • 例えば、データベースから一覧取得するようなストリームは、コネクションタイムアウトエラーを起こし得て、副作用としてデータベースアクセスを要求する、といった形にできます。(そのように設計すれば)
AsyncIterableの例
import { Chunk, Console, Effect, Stream } from "effect";

function* naturals(): Iterablebigint> {
  let n = 0n;
  while (true) {
    yield n++;
  }
}
const stream = Stream.fromIterable(naturals()).pipe(
  Stream.filter((n) => n % 2n === 0n),
  Stream.take(10),
  Stream.map((n) => n * n),
);

stream.pipe(
  Stream.runCollect,
  Effect.map(Chunk.toArray),
  Effect.andThen(Console.log), 
  Effect.runPromise,
);
ReadableStreamの例
import { Chunk, Console, Effect, Stream } from "effect";

const readableStream = new ReadableStreamstring>({
  start(controller) {
    controller.enqueue("Hello, ");
    controller.enqueue("world!");
    controller.close();
  },
});
const streamFromReadable = Stream.fromReadableStream({
  evaluate: () => readableStream,
  onError: (error) => new Error(`Stream error: ${error}`),
});

streamFromReadable.pipe(
  Stream.runCollect,
  Effect.map(Chunk.toArray),
  Effect.andThen(Console.log), 
  Effect.runPromise,
);

Sink

import { Sink } from 'effect';

Streamは Stream.run... 系のもので、ひとつの値へと集計することもできますが、それを特別に表す構造も持ちます。(もしかしたらそれ以上の意味を持つかもしれません。)
Sinkの基本的な利用方法は Stream.run(stream, sink) になりますから、最も一般化された run... 系のベース、と思っても良いかもしれません。

Sinkのタイプパラメータは以下のようになります。

     ┌─── 最終的に、どのような結果に集計されるか
     |  ┌─── 対象として何のストリームを集計するか
     |  |   ┌─── 余り (leftover)
     ▼  ▼   ▼
Sink

たとえば、

  • numberストリームの合計値を計算するなら、 Sink
  • 文字列ストリームの合計サイズをbigintで集計するなら、 Sink
  • booleanストリームの、最初にtrueになる場所のインデックスを取るなら、 Sink

のように設計できますね。最後の例には、leftoverを含んだものになっており、結果を出すのに、すべてを見なくても良い場合に利用できます。
また、その残りをさらに利用するからこそ型として表現されてるわけですが、そのleftoverの利用方法のひとつが次になります。

ところで、 LInnever になる以外で有用なケースはあるのだろうかというのは、私も気になります。基本は、残りがあるのか、ないのか、といったことを示す用途なのでしょうかね。

Stream.transduce

import { Stream } from 'effect';
Stream.transduce(
  stream: Stream.StreamA>,
  sink: Sink.SinkB, A, A>,
): Stream.StreamB>

transduceは、Sinkを利用して、Streamを別のStreamに変換します。
このとき、Sinkは値をいくらか消費して値を出して、同じタイプの値が残って…を繰り返すようなものである必要があります。

先ほどの、booleanストリームの、最初にtrueになるインデックスを返すものを考えると、true同士(と端)の間隔がいくつ空いているか、のストリームが得られます。

booleanストリームのコード例
import { Sink, Stream, Data, Effect, Console, Chunk } from "effect";


const sink = Sink.fold(
  Data.struct({ last: false, count: 0 }),
  ({ last }) => !last,
  ({ count }, e: boolean) =>
    Data.struct({
      last: e,
      count: count + 1,
    }),
).pipe(Sink.map(({ last, count }) => (last ? count - 1 : count)));

const stream = Stream.fromIterable([
  false,
  false,
  false,
  true,
  false,
  false,
  true,
  false,
  false,
  false,
  false,
  false,
  false,
]);

stream.pipe(
  Stream.transduce(sink),
  Stream.runCollect,
  Effect.andThen(Chunk.toArray),
  Effect.andThen(Console.log), 
  Effect.runPromiseExit,
);

Sink.foldWeighted

Sink.foldWeightedはストリームを特定の重みになるまで集めて、例えばチャンクなどにまとめて出します。
ひとつできたら、残りは余り(leftover)となり終了します。

たとえば、 10 を越えない範囲で先頭から集めてチャンクするSinkが次のように書けます。

Sink.foldWeighted({
  initial: Chunk.emptynumber>(),
  maxCost: 10,
  cost: (_acc: Chunk.Chunknumber>, input: number) => input,
  body: (acc: Chunk.Chunknumber>, input: number) => acc.pipe(Chunk.append(input)),
}); 
  • initial: 初期状態 (ここでは初期の空のチャンク)
  • maxCost: 最大コスト。このコストを越えない範囲で最大のチャンクを作る。ただし、最初の要素であれば越えてても追加する。
  • cost: 新たに追加する要素 input 単体のコストを計算する関数。現時点のチャンクの状態も反映される。
  • body: 現在のチャンクに、新たに追加する要素 input を加えたものを返す関数。

ここではチャンクを作ることを前提に書きましたが、チャンクでなくとも一般にどんなオブジェクトでもよいです。そのオブジェクト自体からはコストを算出するための情報が抜けていてもいいわけですから、「合計値が特定の値を越えない範囲の、平均値」のようなものを直接求めるといったこともできます。

さて、Sink.foldWeightedをSteram.transduceと組み合わせて利用すれば、特定のコストを越えない範囲でチャンクしたストリームに変換する、ということができますね。

Effect.Semaphore

Effect.makeSemaphoreを利用して、特定のキャパシティを持つセマフォを作成できます。

セマフォは並列プログラミングなどに用いられるリソース管理機構で、特定のキャパシティを越えない範囲で並列に処理をすることを実現します。
セマフォを作成すると、リソースが利用できるようになるまで待機する .withPermits か、使えなければそれを教えてくれる .withPermitsIfAvailable (オプションを返す) があります。

ここで、10MBに相当するバイト数のキャパシティを持つセマフォを作成し、ひとつのリクエストを送って処理の完了を待つのに必要なキャパシティを、そのリクエストのバイト数としておけば目的が達成できそうです。

実装の外観

完全版は折り畳みで書いておきます。


const semaphore = yield* Effect.makeSemaphore(semaphorePermits);
const scope = yield* Effect.scope;

yield* stream.pipe(
  
  Stream.map((value) =>
    Data.struct({
      value,
      dataBytes: estimateBigQueryValueBytes(value),
    }),
  ),

  
  
  Stream.transduce(
    Sink.foldWeighted({
      initial: Data.struct({
        cost: bqAppendRowsOverheadBytes,
        chunk: Chunk.emptyStream.Stream.Successtypeof stream>>(),
      }),
      maxCost: bqAppendRowsCapBytes,
      cost: (_acc, { dataBytes }) => dataBytes,
      body: (acc, { value, dataBytes }) =>
        Data.struct({
          cost: acc.cost + dataBytes,
          chunk: Chunk.append(acc.chunk, value),
        }),
    }),
  ),

  
  
  
  

  
  Stream.mapEffect(({ chunk, cost, chunkIndex, offset }) =>
    Effect.gen(function* () {
      yield* Effect.log('appendRows権の取得完了(semaphore)', {
        AppendRowsを実行する: {
          chunkIndex,
          行数: Chunk.size(chunk),
          cost,
          offset,
        },
      });
      return yield* Effect.tryPromise(async () => {
        const pw = writer.appendRows(
          chunk.pipe(Chunk.toArray),
          offset,
        );
        return await pw.getResult();
      });
    }).pipe(
      
      semaphore.withPermits(Math.min(cost, semaphorePermits)),
      Effect.forkIn(scope),
      
    ),
  ),
);

全体の流れとして、以下が伝われば十分です。

  1. セマフォを今回のストリーム挿入用に確保。
  2. ストリームのデータを、適切なサイズ以下になるようにチャンクに分割。
  3. チャンクをその推定サイズのキャパシティをセマフォに要求しながら、並列に処理。
    • 合計サイズ上限は越えないように、最大限リクエストを送る。
完全な実装
import type { Table } from '@google-cloud/bigquery';
import { adapt, managedwriter } from '@google-cloud/bigquery-storage';
import type { google } from '@google-cloud/bigquery-storage/build/protos/protos.js';
import {
  Chunk,
  Config,
  Data,
  Effect,
  Fiber,
  Option,
  pipe,
  Schema,
  Sink,
  Stream,
} from 'effect';
import { Status } from 'google-gax';

export class StreamSetupError extends Data.TaggedError('StreamSetupError'){
  message: string;
}> {
  toString() {
    return `${this.name}: ${this.message}`;
  }
}
export class AppendRowsGoogleInternalError extends Data.TaggedError(
  'AppendRowsGoogleInternalError',
){
  readonly googleInternalRpcStatus: google.rpc.IStatus;
}> {
  toString() {
    return `${this.name}: ${this.googleInternalRpcStatus.message} (code: ${this.googleInternalRpcStatus.code})`;
  }
}
export class AppendRowsError extends Data.TaggedError(
  'AppendRowsGoogleInternalError',
){
  readonly rowErrors: google.cloud.bigquery.storage.v1.IRowError[];
}> {
  toString() {
    return `${this.name}: ${this.rowErrors
      .slice(0, 10)
      .map((e) => `${String(e.index)}: ${e.message}`)
      .join(', ')}`;
  }
}


export const BQ_APPEND_ROWS_MAX_BYTES = 10 * 1024 * 1024;
const bqAppendRowsCapBytesConfig = Schema.Config(
  'BQ_APPEND_ROWS_CAP_BYTES',
  Schema.NumberFromString.pipe(
    Schema.int(),
    Schema.between(0, BQ_APPEND_ROWS_MAX_BYTES),
  ).annotations({
    title: 'BigQuery (StorageWrite API) / Append Rows / Cap Bytes',
    description:
      'BigQuery Storage Write APIのAppend Rowsで1回に送信できる最大バイト数',
  }),
).pipe(Config.withDefault( 3 * 1024 * 1024));
const bqAppendRowsBufferBytesConfig = Schema.Config(
  'BQ_APPEND_ROWS_BUFFER_BYTES',
  Schema.NumberFromString.pipe(
    Schema.int(),
    Schema.between(0, BQ_APPEND_ROWS_MAX_BYTES),
  ).annotations({
    title: 'BigQuery (StorageWrite API) / Append Rows / Cap Bytes',
    description:
      'BigQuery Storage Write APIのAppend Rowsの合算リミットに対しもたせておく余裕サイズ',
  }),
).pipe(Config.withDefault( 1 * 1024 * 1024));
const bqAppendRowsOverheadBytesConfig = Schema.Config(
  'BQ_APPEND_ROWS_OVERHEAD_BYTES',
  Schema.NumberFromString.pipe(
    Schema.int(),
    Schema.between(0, BQ_APPEND_ROWS_MAX_BYTES),
  ).annotations({
    title:
      'BigQuery (StorageWrite API) / Append Rows / Overhead Bytes',
    description:
      'BigQuery Storage Write APIのAppend Rowsで1回の送信にかかると想定するオーバーヘッド',
  }),
).pipe(Config.withDefault( 3 * 1024));

const handleAppendResult = ({
  appendResult,
  error,
  rowErrors,
}: google.cloud.bigquery.storage.v1.IAppendRowsResponse) =>
  Effect.gen(function* () {
    if (error != null) {
      
      
      if (error.code === Status.ALREADY_EXISTS) {
        return Option.fromNullable(appendResult);
      }
      return yield* Effect.fail(
        new AppendRowsGoogleInternalError({ googleInternalRpcStatus: error }),
      );
    }

    if (rowErrors != null && rowErrors.length > 0) {
      return yield* Effect.fail(new AppendRowsError({ rowErrors }));
    }
    return Option.fromNullable(appendResult);
  });

export const estimateBigQueryValueBytes = (
  value: AppendRowsInput | AppendRowsInputItem | AppendRowsInputItemValue,
): number => {
  
  return new Blob([JSON.stringify(value)]).size;
};

export const writeStream = (
  table: Table,
  stream: Stream.StreamAppendRowsInputItem>,
) =>
  pipe(
    Effect.gen(function* () {
      const writeClient = new managedwriter.WriterClient();
      const projectId = table.dataset.bigQuery.projectId;
      const datasetId = table.dataset.id;
      const tableId = table.id;
      const destinationTable = `projects/${projectId}/datasets/${datasetId}/tables/${tableId}`;
      yield* Effect.log(`Destination Table: ${destinationTable}`);
      const writeStream = yield* Effect.tryPromise(() =>
        writeClient.createWriteStreamFullResponse({
          streamType: managedwriter.PendingStream,
          destinationTable,
        }),
      ).pipe(
        Effect.mapError(
          (e) => new StreamSetupError({ message: String(e.error) }),
        ),
      );

      const tableSchema = writeStream.tableSchema;
      if (tableSchema == null)
        return yield* Effect.fail(
          new StreamSetupError({
            message: `テーブルスキーマ(tableSchema)が取得できませんでした。テーブルID: ${tableId}`,
          }),
        );
      const protoDescriptor = adapt.convertStorageSchemaToProto2Descriptor(
        tableSchema,
        'root',
      );

      const writeStreamName = writeStream.name;
      if (writeStreamName == null)
        return yield* Effect.fail(
          new StreamSetupError({
            message: `テーブルID '${tableId}' のwriteStream.nameが取得できませんでした`,
          }),
        );

      yield* Effect.log('writeStreamのセットアップ完了', {
        writeStreamName,
      });

      const connection = yield* Effect.tryPromise(() =>
        writeClient.createStreamConnection({
          streamId: writeStreamName,
        }),
      ).pipe(
        Effect.mapError((e) => new StreamSetupError({ message: e.message })),
      );

      const writer = new managedwriter.JSONWriter({
        connection,
        protoDescriptor,
      });
      yield* Effect.addFinalizer((exit) =>
        Effect.gen(function* () {
          yield* Effect.log('StorageWriteApiのストリームを閉じます', {
            exit: exit.toString(),
            writeStreamName,
          });
          writer.close();
        }),
      );

      yield* Effect.log(
        `セットアップ完了: streamId=${connection.getStreamId()}`,
      );

      
      
      const fiber = yield* Effect.fork(
        Effect.gen(function* () {
          while (true) {
            yield* Effect.async((resume) => {
              connection.once('error', (...args) => {
                resume(
                  Effect.log(`Ignoring connection error`, {
                    args,
                  }),
                );
              });
            });
          }
        }),
      );
      yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));

      return [
        connection,
        writer,
        writeClient,
        destinationTable,
        writeStreamName,
      ] as const;
    }),
    Effect.andThen(
      ([connection, writer, writeClient, destinationTable, streamId]) =>
        Effect.gen(function* () {
          const bqAppendRowsCapBytes = yield* bqAppendRowsCapBytesConfig;
          const bqAppendRowsBufferBytes = yield* bqAppendRowsBufferBytesConfig;
          const bqAppendRowsOverheadBytes =
            yield* bqAppendRowsOverheadBytesConfig;

          const semaphorePermits =
            BQ_APPEND_ROWS_MAX_BYTES - bqAppendRowsBufferBytes;
          const semaphore = yield* Effect.makeSemaphore(semaphorePermits);
          const scope = yield* Effect.scope;

          yield* stream.pipe(
            
            Stream.map((value) =>
              Data.struct({
                value,
                dataBytes: estimateBigQueryValueBytes(value),
              }),
            ),

            
            
            Stream.transduce(
              Sink.foldWeighted({
                initial: Data.struct({
                  cost: bqAppendRowsOverheadBytes,
                  chunk: Chunk.emptyStream.Stream.Successtypeof stream>>(),
                }),
                maxCost: bqAppendRowsCapBytes,
                cost: (_acc, { dataBytes }) => dataBytes,
                body: (acc, { value, dataBytes }) =>
                  Data.struct({
                    cost: acc.cost + dataBytes,
                    chunk: Chunk.append(acc.chunk, value),
                  }),
              }),
            ),

            
            Stream.zipWithIndex,
            Stream.map(([{ chunk, cost }, chunkIndex]) =>
              Data.struct({
                chunk,
                cost,
                chunkIndex,
              }),
            ),

            
            Stream.mapAccum(0, (accum: number, v) => [
              accum + Chunk.size(v.chunk),
              Data.struct({
                ...v,
                offset: accum,
              }),
            ]),

            
            Stream.mapEffect(({ chunk, cost, chunkIndex, offset }) =>
              Effect.gen(function* () {
                yield* Effect.log('appendRows権の取得完了(semaphore)', {
                  AppendRowsを実行する: {
                    chunkIndex,
                    行数: Chunk.size(chunk),
                    cost,
                    offset,
                  },
                });
                return yield* Effect.tryPromise(async () => {
                  const pw = writer.appendRows(
                    chunk.pipe(Chunk.toArray),
                    offset,
                  );
                  return await pw.getResult();
                }).pipe(Effect.andThen(handleAppendResult));
              }).pipe(
                Effect.tap(() =>
                  Effect.log(
                    `AppendRowsを実行しました: chunkIndex=${chunkIndex}`,
                  ),
                ),
                semaphore.withPermits(Math.min(cost, semaphorePermits)),
                Effect.forkIn(scope),
                Effect.withSpan('appendRows'),
                Effect.withSpan(`chunkIndex=${chunkIndex}`),
              ),
            ),
            Stream.runCollect,
            Effect.andThen(Fiber.joinAll),
          );

          yield* Effect.log(
            '全てのAppendRowsが完了しました。connection.finalizeを実行します。',
          );
          const rowCount = Option.fromNullable(
            yield* Effect.tryPromise(() => connection.finalize()),
          ).pipe(Option.map((e) => String(e.rowCount)));
          yield* Effect.log(`connection.finalize完了`, { rowCount });

          yield* Effect.log(
            `writeStreamをコミットします。destinationTable: ${destinationTable}, streamId: ${streamId}`,
          );
          const response = yield* Effect.tryPromise(() =>
            writeClient.batchCommitWriteStream({
              parent: destinationTable,
              writeStreams: [streamId],
            }),
          );
          yield* Effect.log('batchCommitWriteStream完了', {
            response,
          });
        }),
    ),
    Effect.withSpan('BigQuery StorageWrite API / writeStream'),
    Effect.withSpan(`対象テーブル=${table.id}`),
    Effect.scoped,
  );

以下のように並列で処理されていくのが確認できる。

...
timestamp=2025-06-19T09:51:35.453Z level=INFO fiber=#34 message="AppendRowsを実行しました: chunkIndex=25"
timestamp=2025-06-19T09:51:35.454Z level=INFO fiber=#37 message=appendRows権の取得完了(semaphore) message="{
  \"AppendRowsを実行する\": {
    \"chunkIndex\": 28,
    \"行数\": 130,
    \"cost\": 3134288,
    \"offset\": 3640
  }
}"
timestamp=2025-06-19T09:51:35.597Z level=INFO fiber=#35 message="AppendRowsを実行しました: chunkIndex=26"
timestamp=2025-06-19T09:51:35.598Z level=INFO fiber=#38 message=appendRows権の取得完了(semaphore) message="{
  \"AppendRowsを実行する\": {
    \"chunkIndex\": 29,
    \"行数\": 130,
    \"cost\": 3134286,
    \"offset\": 3770
  }
}"
timestamp=2025-06-19T09:51:35.724Z level=INFO fiber=#36 message="AppendRowsを実行しました: chunkIndex=27"
timestamp=2025-06-19T09:51:35.724Z level=INFO fiber=#39 message=appendRows権の取得完了(semaphore) message="{
  \"AppendRowsを実行する\": {
    \"chunkIndex\": 30,
    \"行数\": 130,
    \"cost\": 3134289,
    \"offset\": 3900
  }
}"
timestamp=2025-06-19T09:51:35.846Z level=INFO fiber=#37 message="AppendRowsを実行しました: chunkIndex=28"
timestamp=2025-06-19T09:51:35.847Z level=INFO fiber=#40 message=appendRows権の取得完了(semaphore) message="{
  \"AppendRowsを実行する\": {
    \"chunkIndex\": 31,
    \"行数\": 130,
    \"cost\": 3134283,
    \"offset\": 4030
  }
}"
timestamp=2025-06-19T09:51:35.974Z level=INFO fiber=#38 message="AppendRowsを実行しました: chunkIndex=29"
...

省略したこと: リトライ処理

少し長くなってしまうのと、プロジェクト特有の設定にはなってしまうので、リトライ処理は除きました。リトライもEffect.tsでは非常に簡単に記述ができて、大変すばらしいです。
たとえば、 createWriteStreamFullResponse に以下をパイプすることで、リトライ処理を行うことができます。

import { Effect, Schedule, pipe } from 'effect';
Effect.retry({
  schedule: pipe(
    
    Schedule.union(
      Schedule.exponential('1 seconds'),
      Schedule.spaced('20 seconds'),
    ),
    
    Schedule.intersect(Schedule.recurUpTo('15 minutes')),

    scheduleLogRetryCount('createWriteStreamFullResponse'),
  ),
}),

scheduleLogRetryCount は以下のようなスケジュールとのインターセクトを待つ関数を作成します。

import { Effect, Schedule, pipe } from 'effect';

const scheduleLogRetryCount = (spanName: string) =>
  Schedule.intersect(
    pipe(
      Schedule.count,
      Schedule.tapOutput((count) =>
        Effect.gen(function* () {
          if (count > 0) {
            yield* Effect.log(`retry count=${count}`);
          }
        }).pipe(Effect.withLogSpan(spanName)),
      ),
    ),
  );

Schedule.unionSchedule.intersect の意味論は私もまだ深くは理解できていないですが、公式ドキュメントを見ながら、ユーティリティを組み合わせると、他の類似のライブラリパッケージでは簡単にはできなかったことが驚くほど簡易に、かつ強力に実現できます。
これは、本当にすごく感動しています。Effect.tsは、自分の中では、リトライ処理等を行う他の選択肢の苦しさから救ってくれる最良の選択肢になりました。

単なる便利ライブラリとして、そして新しいパラダイムとしてのEffect.ts

この記事で見てきたのはEffect.tsのまだ断片に過ぎません。しかし、単に良くできたパッケージ群だと思っても、非常に強力で新しく価値のあるものであることがわかりました。

Effect.tsのその理論背景にはEffect Systemというものがあるようですが、私はまだ理解の入口にいるかも分かっていません。しかし、それが関数型に由来し、その先に本質的なパラダイムの変革を起こすものであることは間違いなさそうだと感じます。
Promiseが登場したときにasync/awaitの構文が入ったときのように、Effectが登場し、いつか専用の構文が確立される日が来るのかもしれません。 (それはきっとTypeScriptではないんだろうなとは感じますが…)

課題だと思った点

  • とても難しい。ある程度、プログラミングの知識は広くあるつもりでしたが、Effect.ts、そしてEffectそのものへの理解には時間がかかりそうだと感じています。
  • Effect.forkが適切なランタイムで実行されていないことを検知できない。(浮いているFiberをstaticに検知できない)
  • 今回のBigQueryのクライアントみたいなものを、どのようにContextとして抽象化しようか悩ましい(分からない)
    • 逆に言えば、その抽象化をどのように実現しようかということに悩む必要がなくなり、議論すべきアーキテクチャニグに集中できるような気はします。

また、全体がGeneratorになる、とかは、強力な標準が搭載されることによって得られるメリットが圧倒的だと思っています。自由に色々できてしまう世界のほうがツラいです。

宣伝: 君も一緒にEffect.tsを書かないか?

有効期限: 2025年07月31日 (適宜更新します)

ちょうどこの記事の内容のもとになった実装が、プロダクションにリリースされようとしています。すなわち、Effect.tsが部分的にですがプロダクションに出ます。
Effect.tsを導入するために、トップダウンと、ボトムアップの両面から少しずつ変えようとしています。
この記事で紹介したコードもかなりリファクタリングがさらに必要でしょうから、これからが楽しみなところです。

私のチームを含め、いくつかのチームが採用をしています。採用ページ → https://recruit.optimind.tech/

あと、閲覧注意ですが、本当に僕が個人的に、今の会社についてと、コワーカー募集というnote記事を書きました。



Source link

Views: 0

吉成社長、柔道大会優勝!監督兼選手の快挙


サクセスの𠮷成隆杜社長が、2025年6月14日に鹿児島市で開催された「第19回日本マスターズ柔道大会 兼 2025年日本ベテランズ国際柔道大会」の団体戦で、チーム「東京柔道BAKA」の監督兼選手として優勝しました。

大会の概要

この大会は、30代から70代までの選手がそれぞれ1名ずつ参加する5人制の体重無差別ルールで行われ、国内外の全26チームが参加しました。𠮷成氏は70代の選手として出場し、2回戦から決勝までの全4戦で勝利を収め、チームの優勝に大きく貢献しました。

𠮷成氏の実績とコメント

𠮷成氏はこれまで数多くの国際大会で個人戦の優勝経験がありますが、団体戦での全国優勝は今回が初めてです。この勝利について、彼は「三度の飯より柔道が好きなメンバーが集まった“柔道BAKA”として、皆が自分の柔道を貫いた結果が優勝に繋がった」とコメントし、76歳でチームでの勝利の喜びを味わえたことを嬉しく思っています。

𠮷成隆杜 (よしなりたかと) のプロフィール

  • 生年月日: 1949年2月9日(76歳)
  • 出身地: 徳島県
  • 段位・流派: 講道館柔道七段、大東流合気柔術
  • 主な実績:
    • 日本マスターズ柔道大会 優勝 11回
    • 2017年イタリア世界ベテランズ柔道大会 優勝
    • 2023年アブダビ世界ベテランズ柔道大会 優勝
    • 2025年日本マスターズ柔道大会団体戦優勝

𠮷成氏の優勝は、年齢を重ねてもなおスポーツに情熱を注ぎ続けることができるという希望の象徴とも言えるでしょう。

🧠 編集部より:

補足説明

この記事では、𠮷成 隆杜社長が2025年6月14日に開催された「第19回日本マスターズ柔道大会」において、団体戦で優勝したことについて取り上げています。𠮷成氏は76歳という年齢にもかかわらず、自身のチーム「東京柔道BAKA」を率いて、見事に勝利を収めました。

背景情報

この大会は、参加者が30代から70代までの各年齢層から1名ずつ選ばれ、体重別ではなく無差別の形式で行われるため、年齢や体格を超えた戦いが求められます。全26チームが参加し、競技のレベルは非常に高く、各選手の経験と力量が問われる場面が多々あります。𠮷成氏は70代の選手としてエントリーし、優勝へ向けての連勝を果たしました。

豆知識

「BAKA」というチーム名は、柔道に対する情熱を表現しており、選手たちが日々の生活の中でどれだけ柔道を愛し、取り組んでいるかを象徴しています。このような自身のチーム名に対する愛情は、チームワークや結束を強める要素となることがあります。また、𠮷成氏は過去にも個人戦での優勝経験が豊富であることから、その実力と経験はチームの勝利に大いに貢献したことでしょう。

𠮷成 隆杜のプロフィール

𠮷成氏は、日本の柔道界で数々の功績を残している選手であり、段位は講道館柔道七段。この実績により、彼は柔道において非常に高い評価を受けています。近年の国際大会でも活躍しており、特にベテランズ部門においてその強さを証明し続けています。柔道のように、年齢を重ねるにつれて深まる技術や戦略が、彼の成功を支えているのかもしれません。

このように、𠮷成氏の柔道に対する情熱と実績は、年齢を超えた励みとなる存在です。

  • キーワード: 柔道


柔道 をAmazonで探す

柔道着 をAmazonで探す

スポーツシューズ をAmazonで探す


※以下、出典元
▶ 元記事を読む

Views: 0

「国分太一、無期限活動休止!テレ東は無関係」

2025年6月20日、テレビ東京はTOKIOの国分太一(50)について、今後の番組出演を見合わせると発表しました。この背景には、国分に関する特定の問題があるとされますが、テレビ東京の担当者はアナウンスの中で「コンプライアンス上の問題行為は、テレビ東京の番組では確認されていない」と説明しています。 国分は7月9日に放送予定の『テレ東音楽祭2025~夏~』でMCを務めることが決まっていましたが、今後の様子を見守る必要があるため、国分はこの音楽祭には出演しないこととなりました。一方、毎週日曜午前11時から放送中の『男子ごはん』については、国分の出演部分を差し替えることが発表されています。現在、同番組の新しい内容については検討中とのことです。 以下の画像は国分太一の関連情報に伴うものです。
国分の今後の動向については、多くの視聴者やファンが注目しています。テレビ東京は、国分の出演に関して適切な判断を求めている模様です。

🧠 編集部より:
この記事は、TOKIOのメンバーである国分太一さんが、テレビ東京の番組への出演を見合わせることになったという発表について報じています。テレビ東京は、コンプライアンス上の問題行為が同局の番組で確認されていないとしています。国分さんは『テレ東音楽祭2025~夏~』のMCとしての出演が予定されていましたが、それは変更なく放送される一方、『男子ごはん』は他の内容に差し替えられることが決定しました。 ### 背景や豆知識 国分太一さんは、アイドルグループTOKIOのメンバーとしてだけでなく、テレビのバラエティ番組での司会や料理番組での人気プレゼンターとしても知られています。『男子ごはん』は、彼が料理を通じて様々なレシピを提案する人気番組で、これまで多くの家庭で親しまれてきました。 なお、出演者がコンプライアンスの問題で活動を自粛する際、テレビ業界全体がこの問題に対して敏感になっており、視聴者からの反応も多岐にわたります。国分さんの場合、プライベートの問題ではないことが伝えられており、ファンにとっては少し安心材料となる部分もあるでしょうね。


  • キーワード: 国分太一
テレビ東京 をAmazonで探す 国分太一 をAmazonで探す 男子ごはん をAmazonで探す

※以下、出典元 ▶ 元記事を読む

Views: 1

「Dune: Awakening」,同時接続プレイヤーが19万人超に到達。同スタジオによる「Conan Exiles」の最高記録を塗り替える



 Funcomは2025年6月20日,マルチプレイヤーサバイバルゲーム「Dune: Awakening」の同時接続プレイヤーが,同スタジオの最高記録を上回る19万人以上に達したことを発表した。



 Dune: Awakeningは,1965年に発表されたSF小説「デューン砂の惑星」と,それをベースに2021年に公開された映画「DUNE/デューン 砂の惑星」を原作としたマルチプレイヤーサバイバルゲームだ。プレイヤーは惑星「アラキス」に降り立ち,メランジと呼ばれる超物質を中心とした争いに身を投じることになる。

画像ギャラリー No.001のサムネイル画像 / 「Dune: Awakening」,同時接続プレイヤーが19万人超に到達。同スタジオによる「Conan Exiles」の最高記録を塗り替える

 発表によると,開発チームはゲーム内環境の改善や,物語と機能を拡張する大規模な無料アップデートの準備に取り組んでいるとのこと。4Gamerでは開発チームへのインタビューやプレイレポートを掲載しているので,気になる人はチェックしてみよう。

関連記事


PC版「Dune: Awakening」,正式配信をSteamで開始。家庭用ゲーム機版は2026年内にリリースへ



PC版「Dune: Awakening」,正式配信をSteamで開始。家庭用ゲーム機版は2026年内にリリースへ


 Funcomは2025年6月10日,PC版「Dune: Awakening」を,Steamで正式リリースした。本作は,フランク・ハーバートが1965年に発表した小説「デューン砂の惑星」ならびに,それをベースにした映画「デューン 砂の惑星」原作とするタイトルだ。プレイヤーは,地球から遠く離れた惑星「アラキス」を舞台にサバイバルを繰り広げていく。


[2025/06/10 23:00]

関連記事


サンドボックス型MMOへの回帰を目指して。「Dune: Awakening」の開発者に,原作へのこだわりや見どころを聞いた



サンドボックス型MMOへの回帰を目指して。「Dune: Awakening」の開発者に,原作へのこだわりや見どころを聞いた


 Funcomが開発を手がける「Dune: Awakening」のPC版が,2025年6月10日に正式リリースを迎えた。この発売に際して行った開発者へのメールインタビューをお届けする。SFジャンルの金字塔たる「デューン」を原作とした理由や,そこへのこだわりを,クリエイティブ・ディレクターのJoel Bylos氏に聞いた。


[2025/06/18 10:00]

「Dune: Awakening」公式サイト

砂の惑星で繰り広げられるサバイバルゲーム
『Dune: Awakening』
約19万人が同時接続、ついに“黄金の道”が始動
『Dune: Awakening』に絶賛の嵐!
最新トレーラーで各メディアが称賛!

 Funcomが開発を手がけたオープンワールド型の大規模マルチプレイヤー・サバイバルゲーム『Dune: Awakening』は、6月10日(火)の発売以来好評を博しており、Steamでは28,700件を超えるレビューを獲得。総合レビューでは「非常に好評(85%)」の評価を得ています。また、Metacritic では80点、OpenCritic においては81点の評価を獲得しています。
Dune: Awakening – Accolades Trailer
https://www.youtube.com/watch?v=fdgg-X0KDXM

 発売直後の週末に『Dune: Awakening』は同時接続プレイヤー数の自己記録を3度にわたって更新し、過去最高の189,000人超を記録しました。これは、Funcomスタジオの前作『Conan Exiles』の記録である53,400人を大きく上回る数字です。また本作は、Funcom史上最速のセールスを記録したタイトルともなりました。先行アクセス開始以来、『Dune: Awakening』はSteam上で常に最もプレイされているゲームのひとつに名を連ねています。
 プレイヤー、メディア、コンテンツクリエイターからの反響がネット上で爆発的に広がる中、特に注目されているのが本作の核となる要素です。意図的に構築されたパラレルタイムライン、<デューン>ユニバースへの忠実さ、そしてドゥニ・ヴィルヌーヴ監督とレジェンダリーの映画作品から着想を得た視覚・聴覚面の表現が、その魅力を際立たせています。
 VGMAGは『Dune: Awakening』に 8.5/10 のスコアをつけ、“アラキスを体験する最良の手段”と称賛しました。4Pにおいては9/10の評価で、“この作品以上に<デューン>的なゲーム世界は知らない”と述べ、まるで実世界のように広大なアラキスを何年も待ち望んでいたファンにとって、『Dune: Awakening』はその期待に応える作品となりました。

画像ギャラリー No.002のサムネイル画像 / 「Dune: Awakening」,同時接続プレイヤーが19万人超に到達。同スタジオによる「Conan Exiles」の最高記録を塗り替える

 没入感あふれる“本物のアラキス”の世界観だけが、批評家たちを魅了したわけではありません。IGNのレビュースコアは『Dune: Awakening』に 8/10 、Eurogamerは 4/5 の評価とともに「卓越したサバイバルゲームプレイ」と称賛。Screenrantは「夢中になりすぎて、食事を抜いてしまった」とコメントし、DualShockersは9/10のスコアとともに、“『Dune: Awakening』は、10年後に振り返ったとき「史上最高のゲームのひとつ』だと語られるような作品になると確信している”とまで断言しています。
 これはまだ始まりに過ぎません。『Dune: Awakening』のシーズンパスには、最初の4つのDLCが含まれており、開発チームはすでに今後の改善や、物語と機能を拡張する大規模な無料アップデートの準備にも取り組んでいます。
『Dune: Awakening』は現在、Steamにて好評発売中です。
Steamストア:https://store.steampowered.com/app/1172710/Dune_Awakening/?l=japanese
日本語版公式X
https://x.com/duneawakeningjp
英語版公式ウェブサイト
https://duneawakening.com/en
英語版公式SNS
X:https://x.com/DuneAwakening
Facebook:https://www.facebook.com/DuneAwakening
Instagram:https://www.instagram.com/duneawakening/
TikTok:https://www.tiktok.com/@duneawakening
Discord:https://discord.com/invite/duneawakening
■Funcomについて
 Funcomは、PCおよびコンソール向けゲームの開発と販売を行っています。1993年の創業以来、20以上のタイトルをリリースし、優れたエンターテインメントを提供し続けています。代表作には、『Conan Exiles』、『Metal: Hellsinger』、『Aloft』、『Dune: Spice Wars』、『Secret World Legends』、『Age of Conan: Hyborian Adventures』、『The Longest Journey』、『Anarchy Online』、『Dreamfall: The Longest Journey』などがあります。Funcomに関する詳細はwww.funcom.comをご覧ください。
■LEGENDARY ENTERTAINMENT について
 LEGENDARY ENTERTAINMENT(レジェンダリー・エンターテインメント)は、映画(レジェンダリー・ピクチャーズ)、テレビ・デジタル(レジェンダリー テレビジョン&デジタルメディア)、コミック(レジェンダリー・コミックス)の各部門を持ち、世界中の視聴者にコンテンツを所有、制作、運用する大手メディア企業です。レジェンダリーは、大規模上映作品のライブラリを構築し、世界で最も人気のあるIPを含む高品質のエンターテインメントを一貫して提供するブランドとしての地位を確立しています。レジェンダリー・ピクチャーズ関連の作品は、全世界で約210億ドルの興行収入を達成しています。詳細については、以下をご覧ください。https://www.legendary.com
■フランク・ハーバートについて
 フランク・ハーバート(1920年-1986年)は、SF文学史上最も愛される小説『デューン』の作者であり、同作はヒューゴー賞およびネビュラ賞を受賞しました。彼は多才で、複雑な思考を持つ人物でした。その集大成ともいえる『デューン』は、あらゆるジャンルの中でも最も多層的で奥深い作品のひとつとして知られています。昨今、『デューン』はこれまで以上に人気を集め、新たな読者が次々と物語の魅力に触れ、友人に薦めることでその輪が広がり続けています。本作は40以上の言語に翻訳され、世界中で数千万部以上の売り上げを達成。現在、彼の文学的遺産は息子のブライアン・ハーバートと、孫のキム・ハーバート、バイロン・メリットによって管理されています。





続きを見る


🧠 編集部の感想:
「Dune: Awakening」が同時接続プレイヤー19万人超は驚異的な成果ですね。SF小説と映画に基づくゲームがここまで支持を集めるとは、ファンの熱意を感じます。さらなるアップデートにも期待大で、この勢いが続くことを願います。

Views: 0

「夏にぴったり!冷感ポケモンぬいぐるみ登場」

2025年6月28日、ポケモンセンターにて、こおりタイプのポケモンたちをテーマにした「ひんやりぬいぐるみ」が発売されます。これに先駆けて、ポケモンセンターオンラインでは6月26日10時から販売を開始します。

商品概要

ラインナップは、タマザラシ、バニプッチ、カチコール、ユキハミの4匹です。特にバニプッチとユキハミは等身大サイズで登場します。これらのぬいぐるみは、サラサラでなめらかな接触冷感生地を使用していて、暑い日に快適なひんやり感を楽しむことができます。価格は各4950円(税込)です。

販売店舗

  • ポケモンセンター全店
  • ポケモンセンターオンライン
  • Amazon.co.jp[ポケモンストア]

※発売日以降は他のイベント会場でも取り扱いがある可能性があります。

重要なポイント

  • 接触冷感生地: 暑い夏にぴったりの製品で、リラックスタイムを楽しむのに最適です。
  • 価格: 各4950円(税込)で、非常に手ごろな価格設定となっています。

商品の詳細については、ポケモン公式サイトをご覧ください。

ひんやりぬいぐるみ タマザラシ
ひんやりぬいぐるみ タマザラシ 4950円
ひんやりぬいぐるみ バニプッチ
ひんやりぬいぐるみ 等身大バニプッチ 4950円

この夏、ひんやりぬいぐるみで涼しさを楽しみましょう!

🧠 編集部より:

この記事のテーマは、「ポケモンのひんやりぬいぐるみ」に関連しています。以下に補足説明をお届けします。

補足説明

2025年6月28日から、ポケモンセンターにて発売されるのは「ひんやりぬいぐるみ」です。これは、こおりタイプのポケモンであるタマザラシ、バニプッチ、カチコール、ユキハミをテーマにした商品です。特に注目すべきは、バニプッチとユキハミが等身大サイズで登場する点です。すべてのぬいぐるみは、接触冷感の特性を持った生地で作られており、高温多湿が続く夏でも心地よいひんやり感を楽しむことができます。価格は各4950円(税込)です。

ちょっとした背景や豆知識

  • 接触冷感生地とは、肌に触れることで熱を奪い、涼しさを感じさせる素材のことで、特に夏に人気です。
  • ポケモンの多様性:ポケモンは多くの種類とタイプが存在し、それぞれが異なる特性を持っています。こおりタイプは寒さや凍結をテーマにしたポケモンが多く、ファンにとっては魅力的な存在です。
  • ぬいぐるみの人気:ポケモンのぬいぐるみは、単なるおもちゃではなく、コレクターズアイテムとしても強い人気を誇ります。季節に合わせた商品展開は、ファンの購買欲を掻き立てる要因となっています。

このように、ポケモンのひんやりぬいぐるみは、暑い夏を乗り切るための心強い味方になりそうです!

  • キーワード: ひんやりぬいぐるみ

ひんやりぬいぐるみ タマザラシ をAmazonで探す

ひんやりぬいぐるみ 等身大バニプッチ をAmazonで探す

ひんやりぬいぐるみ カチコール をAmazonで探す



※以下、出典元
▶ 元記事を読む

Views: 0

「ピカチュウのカフェグッズ、6月28日発売!」

2025年6月20日に公開されたニュースによると、ポケモンは「Pokemon Cafe & Pikachu Sweets」をテーマにした新しいグッズを、全国のポケモンセンターにて6月28日に発売します。

新発売のポケモングッズ

新しい商品には、以下のような魅力的なアイテムが含まれています:

  • カフェプレート風ぬいぐるみ(ピカチュウ):特に注目されているのは、「カレーのおふとん」が付属するぬいぐるみです。
  • フラッペ風ペンケース(イーブイ):こちらはバッグに取り付けられるデザインで、実用性と可愛さが両立しています。
  • その他、ポケモンたちがプリントされたTシャツや雑貨も展開され、多様な選択肢から選ぶことができます。

購入方法

これらのアイテムは、全国のポケモンセンターで6月28日に発売される予定ですが、ポケモンセンターフクオカでは、プレオープン日である6月26日から販売が開始されます。また、通販サイト「ポケモンセンターオンライン」では、同日10時よりオンラインでの取り扱いも開始されます。

まとめ

これら新商品のリリースは、ポケモンファンにとって待望のイベントです。特に「Pokemon Cafe & Pikachu Sweets」は、可愛いデザインと実用性を兼ね備えており、幅広い層の支持が期待されます。ポケモンセンターでの購入やオンラインショッピングにぜひご注目ください。

🧠 編集部より:

ポケモンカフェ&ピカチュウスイーツ グッズの発売

ポケモンセンターが企画した「Pokemon Cafe & Pikachu Sweets」に関連する新しいグッズが、2024年6月28日に全国のポケモンセンターで発売されます。このコレクションには、愛らしいデザインが豊富な商品がラインナップされており、特に注目なのが「カフェプレート風ぬいぐるみ ピカチュウ」と「フラッペ風ペンケース イーブイ」です。これらの商品は、ファン必見のアイテムです。

商品ラインナップのご紹介

  1. カフェプレート風ぬいぐるみ ピカチュウ

    • ピカチュウもちょっとユニークなスタイルで、可愛いカレーのおふとんが付属しています。
  2. フラッペ風ペンケース イーブイ

    • バッグにつけやすい形で、学校や仕事場でも使えます。
  3. Tシャツや雑貨
    • ポケモンたちがデザインされたTシャツや様々な雑貨も展開されています。

発売日と購入方法

これらの新商品は、全国のポケモンセンターで6月28日から発売され、その前にポケモンセンターフクオカにてプレオープンが行われる6月26日から購入可能です。また、オンライン購入については「ポケモンセンターオンライン」で6月26日午前10時から取り扱いが始まります。

背景や豆知識
  • ポケモンカフェは、ファンがポケモンの世界観を楽しむための特別なカフェです。特製メニューも魅力の一つで、ポケモンのキャラクターをテーマにした料理やデザートが提供されています。
  • 色々なポケモングッズは、コレクターにとっても人気があり、発売後はすぐに売り切れてしまうことが多いので、興味がある方は早めに購入することをおすすめします。

関連ページへのリンク

これを機に、ぜひポケモンの魅力に触れてみてはいかがでしょうか!

  • キーワード: グッズ発売

カフェプレート風ぬいぐるみ ピカチュウ をAmazonで探す

フラッペ風ペンケース イーブイ をAmazonで探す

ポケモン Tシャツ をAmazonで探す



※以下、出典元
▶ 元記事を読む

Views: 0

「CIO、Qi2対応の新充電器登場!」

CIOは2025年6月20日に、無線と有線の同時充電に対応する新しい充電器「NovaWave SPOT PLUG +C」を発表しました。実売価格は6,980円となっています。

製品の特徴

この充電器は、最新のQi2規格に基づき、無線充電とUSB Type-Cによる有線充電を同時に行うことができます。具体的には、最大出力は以下の通りです:

  • 有線充電時: 最大30W
  • 無線充電時: 最大15W
  • 同時使用時: 最大25W

充電規格にはPD 3.0/2.0やPPSが対応しており、幅広いデバイスに対応できます。

コンパクトなデザイン

本体のサイズは約58×28mm(直径×高さ)、重量は約108gと非常にコンパクトで、持ち運びやすいデザインが特徴です。

充電の様子

以下の画像では、実際にiPhoneをQi2で充電している様子や、USB Type-Cケーブルを使用して最大30Wで充電しているシーンを示しています。

設置例
設置例

iPhoneをQi2で充電しているところ
iPhoneをQi2で充電しているところ

USB Type-Cケーブルによる充電では最大30W出力が可能
USB Type-Cケーブルによる充電では最大30W出力が可能

まとめ

「NovaWave SPOT PLUG +C」は、複数のデバイスを効率よく充電できる便利なアイテムであり、特にワイヤレス充電が必要なユーザーにとって有用な選択肢となるでしょう。ぜひ、この製品を通じて快適な充電ライフを体験してみてください。

🧠 編集部より:

NovaWave SPOT PLUG +Cについての補足説明

CIOが発表した「NovaWave SPOT PLUG +C」は、最新の充電技術を取り入れた革新的な充電器です。以下に、その特徴や背景について詳しく説明します。

特徴

  1. 同時充電対応

    • Qi2規格に基づく無線充電と、USB Type-Cによる有線充電が同時に可能で、便利な使い方ができます。
  2. 高出力

    • 有線接続時は最大30W、無線接続時は最大15W、そして同時使用時でも25Wの出力を誇ります。この出力は、スマートフォンやタブレットなどのデバイスを短時間で充電可能にします。
  3. コンパクト設計

    • サイズは約58×28mm、重さは約108gと、持ち運びにも便利なサイズです。
  4. 充電規格
    • PD 3.0および2.0、PPSにも対応しており、さまざまなデバイスを効率よく充電できます。

背景と豆知識

  • Qiワイヤレス充電:
    Qi(チー)という名称は、中国語で「エネルギー」を意味しており、この技術は2010年以来、急速に普及しています。現在では多くのスマートフォンやデバイスがこの規格に対応しているため、利便性が高まっています。

  • USB Type-C:
    USB Type-Cは、リバーシブルコネクタを採用しているため、接続が非常に簡単です。また、データ転送速度も非常に速く、充電能力が高いため、多くの新しいデバイスで採用されています。

  • 持ち運びに便利:
    コンパクトなデザインは、旅行や外出時にも重宝します。特に、ワイヤレス充電機能は、ケーブルを探したり絡ませたりすることなく、簡単にデバイスを充電できるのが魅力です。

関連リンク

この充電器は、最新の充電ニーズに応えた製品であり、特に多様な接続オプションを必要とするユーザーにとって非常に便利な選択肢です。

  • キーワード: 充電器

NovaWave SPOT PLUG +C をAmazonで探す



※以下、出典元
▶ 元記事を読む

Views: 0

「Nextorage、革新のmicroSD Express参入!」

Nextorage、最新のmicroSD Expressメモリーカードを発売予定

2025年6月20日、Nextorage株式会社が新しいmicroSD Express規格のメモリーカードを発表しました。これにより、データ転送の高速化や性能向上が期待されます。販売はAmazonにて、128GBおよび256GBモデルが6月23日から受注開始される予定で、秋には512GBと1TBのモデルも登場する見込みです。

microSD Expressの特徴

microSD Expressは、SDアソシエーションが策定した最新のメモリーカード規格で、PCIeおよびNVMeインターフェースを採用。これにより、従来のmicroSDメモリーカードと比較して大幅な速度向上が見込まれています。特に、6月5日に発売された「Nintendo Switch 2」がこの規格を採用したことで、多くの注目を集めています。

スペックとパフォーマンス

Nextorageが投入する新カードは、「Gシリーズ EX」という名称で、既存の「Gシリーズ」の上位モデルと位置づけられています。UHS-I、U3、V30といった速度規格に準拠しており、最大読み出し転送速度は810MB/秒を誇ります。また、アプリケーションパフォーマンスクラスA1に対応しているため、アプリの起動やデータの読み込みもスムーズです。ただし、書き込み速度についてはまだ明らかになっていません。

耐久性能

このメモリーカードは、IPX7防水、IP5X防塵、耐衝撃性能が1.5mといった優れた耐久性能を持ち、さらには85℃までの高温にも耐えることができます。これにより、過酷な環境でも安心して使用できることが期待されます。

まとめ

NextorageのmicroSD Expressメモリーカードは、最新技術を搭載し、高速転送や耐久性に優れた製品です。特に、Nintendo Switch 2との連携により、今後の利用シーンが広がりそうです。新しいストレージ技術に興味がある方は、発売日を忘れずチェックしておいてください。

microSD Expressメモリーカード をAmazonで探す

Nintendo Switch 2 をAmazonで探す

Gシリーズ EX をAmazonで探す

🧠 編集部より:

Nextorage株式会社が新たに発表したmicroSD Express規格のメモリーカードは、最新の技術を採用し、ユーザーのニーズに応える製品となっています。このカードは、PCIeおよびNVMeインターフェースを利用することで、高速データ転送を可能にし、最大810MB/秒という読み出し速度を実現しています。これは、特に動画や高解像度画像などの大量データを扱うユーザーにとって大きなメリットです。

背景と豆知識

  • microSD Expressの登場: この新しい規格は、SDアソシエーションによって策定され、Flashメモリーの性能を最大限に引き出す方向に進化しています。これにより、ゲームコンソールやプロフェッショナルカメラでの利用が期待されています。
  • Nintendo Switch 2: 最近の話題として、6月に発売された「Nintendo Switch 2」がこのmicroSD Expressを外部メディアとして採用したことが、業界におけるその重要性を強調しています。これにより、データストレージのニーズが飛躍的に高まっているのです。

特徴

  • 耐久性能: 新しいカードは、IPX7防水、IP5X防塵、耐衝撃性(1.5m)、耐高温(最大85℃)といった高い耐久性能を備えており、過酷な環境でも安心して使用できます。
  • アプリケーションパフォーマンスクラス: A1対応により、アプリケーションの読み込み速度が向上し、モバイルデバイスでの使用もスムーズです。

関連リンク

新しい技術が進化する中で、次世代のデータストレージの可能性が広がっています。動画撮影やゲーミング、さらにはデータのバックアップに至るまで、microSD Expressがどのように役立つのか、今後の動向に注目です。

  • 「microSD Express」


※以下、出典元
▶ 元記事を読む

Views: 1